code-coordinator 0.5.46__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_coordinator-0.5.46.dist-info/METADATA +625 -0
- code_coordinator-0.5.46.dist-info/RECORD +295 -0
- code_coordinator-0.5.46.dist-info/WHEEL +5 -0
- code_coordinator-0.5.46.dist-info/entry_points.txt +2 -0
- code_coordinator-0.5.46.dist-info/licenses/LICENSE +110 -0
- code_coordinator-0.5.46.dist-info/top_level.txt +1 -0
- coord/__init__.py +176 -0
- coord/_board_mapping.py +229 -0
- coord/acceptance.py +468 -0
- coord/acceptance_drivers.py +632 -0
- coord/agent.py +7517 -0
- coord/agent_app.py +1555 -0
- coord/agent_update.py +417 -0
- coord/agents/opencode/.gitignore +13 -0
- coord/agents/opencode/agents/work.md +129 -0
- coord/agents/opencode/routing.jsonc +49 -0
- coord/audit.py +301 -0
- coord/auto_loop.py +1440 -0
- coord/board_bool_guard.py +72 -0
- coord/board_service.py +141 -0
- coord/board_wire.py +309 -0
- coord/brain.py +581 -0
- coord/branch_model.py +214 -0
- coord/cargo_cache.py +258 -0
- coord/ci_github.py +386 -0
- coord/ci_store.py +560 -0
- coord/claim.py +353 -0
- coord/cli.py +454 -0
- coord/client.py +610 -0
- coord/commands/__init__.py +1 -0
- coord/commands/_common.py +329 -0
- coord/commands/acceptance.py +916 -0
- coord/commands/agent_ops.py +1339 -0
- coord/commands/audit.py +131 -0
- coord/commands/chat.py +320 -0
- coord/commands/dispatch.py +1780 -0
- coord/commands/dispatch_workers.py +4894 -0
- coord/commands/drive.py +616 -0
- coord/commands/drive_queue.py +1203 -0
- coord/commands/gate_a.py +217 -0
- coord/commands/gates.py +89 -0
- coord/commands/issues.py +681 -0
- coord/commands/lifecycle.py +513 -0
- coord/commands/merge.py +1900 -0
- coord/commands/milestone.py +2081 -0
- coord/commands/plan_followup.py +1243 -0
- coord/commands/plans.py +156 -0
- coord/commands/release.py +2232 -0
- coord/commands/report.py +341 -0
- coord/commands/review.py +1523 -0
- coord/commands/scorecard.py +252 -0
- coord/commands/sessions.py +1930 -0
- coord/commands/setup.py +576 -0
- coord/commands/status.py +2089 -0
- coord/commands/terminal.py +385 -0
- coord/commands/test_gate.py +775 -0
- coord/commands/tui.py +288 -0
- coord/comments.py +718 -0
- coord/config.py +3032 -0
- coord/conflict_fix.py +633 -0
- coord/dao.py +483 -0
- coord/dashboard/__init__.py +0 -0
- coord/dashboard/fixture.py +376 -0
- coord/dashboard/index.html +658 -0
- coord/dashboard/server.py +1894 -0
- coord/dashboard/terminal.py +382 -0
- coord/dashboard/webapp/.gitignore +9 -0
- coord/dashboard/webapp/components.json +17 -0
- coord/dashboard/webapp/dist/assets/Gallery-da3qNiIw.js +71 -0
- coord/dashboard/webapp/dist/assets/Terminal-9CEnUXvW.css +32 -0
- coord/dashboard/webapp/dist/assets/Terminal-skVFCxPU.js +63 -0
- coord/dashboard/webapp/dist/assets/index-DltfZR5f.js +184 -0
- coord/dashboard/webapp/dist/assets/index-Dq4kwTdw.css +1 -0
- coord/dashboard/webapp/dist/assets/workbox-window.prod.es5-BqEJf4Xk.js +2 -0
- coord/dashboard/webapp/dist/icons/icon-192.png +0 -0
- coord/dashboard/webapp/dist/icons/icon-512.png +0 -0
- coord/dashboard/webapp/dist/icons/icon.svg +5 -0
- coord/dashboard/webapp/dist/index.html +38 -0
- coord/dashboard/webapp/dist/manifest.webmanifest +1 -0
- coord/dashboard/webapp/dist/sw.js +1 -0
- coord/dashboard/webapp/dist/workbox-e4022e15.js +1 -0
- coord/dashboard/webapp/e2e/available-gates-terminal.spec.ts +75 -0
- coord/dashboard/webapp/e2e/deep-link.spec.ts +172 -0
- coord/dashboard/webapp/e2e/fixtureServer.ts +155 -0
- coord/dashboard/webapp/e2e/live-update-fixture.spec.ts +113 -0
- coord/dashboard/webapp/e2e/realtime.spec.ts +238 -0
- coord/dashboard/webapp/e2e/shell.spec.ts +309 -0
- coord/dashboard/webapp/e2e/smoke.spec.ts +191 -0
- coord/dashboard/webapp/e2e/terminal.spec.ts +420 -0
- coord/dashboard/webapp/e2e/theme.spec.ts +138 -0
- coord/dashboard/webapp/eslint.config.js +20 -0
- coord/dashboard/webapp/index.html +37 -0
- coord/dashboard/webapp/node_modules/flatted/python/flatted.py +144 -0
- coord/dashboard/webapp/package-lock.json +10584 -0
- coord/dashboard/webapp/package.json +63 -0
- coord/dashboard/webapp/playwright.acceptance.config.ts +166 -0
- coord/dashboard/webapp/playwright.config.ts +93 -0
- coord/dashboard/webapp/postcss.config.js +6 -0
- coord/dashboard/webapp/public/icons/icon-192.png +0 -0
- coord/dashboard/webapp/public/icons/icon-512.png +0 -0
- coord/dashboard/webapp/public/icons/icon.svg +5 -0
- coord/dashboard/webapp/src/App.tsx +140 -0
- coord/dashboard/webapp/src/api/client.ts +199 -0
- coord/dashboard/webapp/src/api/generated.ts +176 -0
- coord/dashboard/webapp/src/components/ConnectionBadge.tsx +52 -0
- coord/dashboard/webapp/src/components/Detail.tsx +800 -0
- coord/dashboard/webapp/src/components/Gallery.tsx +341 -0
- coord/dashboard/webapp/src/components/Home.tsx +435 -0
- coord/dashboard/webapp/src/components/MobileKeyBar.tsx +280 -0
- coord/dashboard/webapp/src/components/PanelHeader.tsx +59 -0
- coord/dashboard/webapp/src/components/PipelineCard.tsx +168 -0
- coord/dashboard/webapp/src/components/SessionCard.tsx +99 -0
- coord/dashboard/webapp/src/components/SessionDetail.tsx +140 -0
- coord/dashboard/webapp/src/components/SessionsList.tsx +81 -0
- coord/dashboard/webapp/src/components/Terminal.tsx +376 -0
- coord/dashboard/webapp/src/components/__tests__/ConnectionBadge.test.tsx +81 -0
- coord/dashboard/webapp/src/components/__tests__/Detail.test.tsx +680 -0
- coord/dashboard/webapp/src/components/__tests__/Gallery.test.tsx +83 -0
- coord/dashboard/webapp/src/components/__tests__/Home.test.tsx +271 -0
- coord/dashboard/webapp/src/components/__tests__/MobileKeyBar.test.tsx +197 -0
- coord/dashboard/webapp/src/components/__tests__/PipelineCard.test.tsx +143 -0
- coord/dashboard/webapp/src/components/__tests__/SessionCard.test.tsx +106 -0
- coord/dashboard/webapp/src/components/__tests__/Terminal.test.tsx +504 -0
- coord/dashboard/webapp/src/components/ui/badge.tsx +41 -0
- coord/dashboard/webapp/src/components/ui/button.tsx +54 -0
- coord/dashboard/webapp/src/components/ui/card.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/dialog.tsx +99 -0
- coord/dashboard/webapp/src/components/ui/dropdown-menu.tsx +189 -0
- coord/dashboard/webapp/src/components/ui/empty-state.tsx +35 -0
- coord/dashboard/webapp/src/components/ui/sheet.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/skeleton.tsx +9 -0
- coord/dashboard/webapp/src/components/ui/tabs.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/theme-provider.tsx +78 -0
- coord/dashboard/webapp/src/components/ui/theme-toggle.tsx +20 -0
- coord/dashboard/webapp/src/components/ui/toast.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/toaster.tsx +30 -0
- coord/dashboard/webapp/src/components/ui/tooltip.tsx +26 -0
- coord/dashboard/webapp/src/components/ui/use-toast.ts +134 -0
- coord/dashboard/webapp/src/index.css +210 -0
- coord/dashboard/webapp/src/lib/pipeline.ts +29 -0
- coord/dashboard/webapp/src/lib/utils.ts +6 -0
- coord/dashboard/webapp/src/main.tsx +46 -0
- coord/dashboard/webapp/src/realtime/RealtimeProvider.tsx +112 -0
- coord/dashboard/webapp/src/realtime/__tests__/RealtimeProvider.test.tsx +189 -0
- coord/dashboard/webapp/src/realtime/__tests__/connection.test.ts +255 -0
- coord/dashboard/webapp/src/realtime/connection.ts +227 -0
- coord/dashboard/webapp/src/realtime/events.ts +100 -0
- coord/dashboard/webapp/src/routes/__tests__/paths.test.ts +92 -0
- coord/dashboard/webapp/src/routes/paths.ts +92 -0
- coord/dashboard/webapp/src/shell/ActivityRail.tsx +335 -0
- coord/dashboard/webapp/src/shell/AppShell.tsx +276 -0
- coord/dashboard/webapp/src/shell/ComingSoon.tsx +33 -0
- coord/dashboard/webapp/src/shell/EmptyDetail.tsx +26 -0
- coord/dashboard/webapp/src/shell/RouteNotFound.tsx +33 -0
- coord/dashboard/webapp/src/shell/ShellLayout.tsx +147 -0
- coord/dashboard/webapp/src/shell/StatusBar.tsx +46 -0
- coord/dashboard/webapp/src/shell/__tests__/ShellLayout.test.tsx +520 -0
- coord/dashboard/webapp/src/shell/__tests__/shellState.test.ts +95 -0
- coord/dashboard/webapp/src/shell/__tests__/stubViewport.ts +40 -0
- coord/dashboard/webapp/src/shell/breakpoints.ts +87 -0
- coord/dashboard/webapp/src/shell/railItems.ts +105 -0
- coord/dashboard/webapp/src/shell/shellState.ts +174 -0
- coord/dashboard/webapp/src/shell/useRegionFocus.ts +95 -0
- coord/dashboard/webapp/src/test-setup.ts +41 -0
- coord/dashboard/webapp/src/vite-env.d.ts +2 -0
- coord/dashboard/webapp/tailwind.config.js +140 -0
- coord/dashboard/webapp/tsconfig.json +25 -0
- coord/dashboard/webapp/tsconfig.node.json +11 -0
- coord/dashboard/webapp/vite.config.ts +71 -0
- coord/db.py +1076 -0
- coord/dead_end.py +332 -0
- coord/deploy/README.md +33 -0
- coord/deploy/coord-agent.service +89 -0
- coord/deploy/coord-db-backup.service +60 -0
- coord/deploy/coord-db-backup.sh +74 -0
- coord/deploy/coord-db-backup.timer +18 -0
- coord/deploy/coord-drive-queue.service +117 -0
- coord/deploy/coord-drive-queue.timer +39 -0
- coord/deploy/coord-notify.service +48 -0
- coord/deploy/coord-notify.timer +24 -0
- coord/deploy/coord-release-propagate.service +83 -0
- coord/deploy/coord-release-propagate.timer +38 -0
- coord/deploy/coord-release-window.service +119 -0
- coord/deploy/coord-release-window.timer +36 -0
- coord/deploy/coord-serve.service +82 -0
- coord/deploy/coord-web-dist-build.service +43 -0
- coord/deploy/coord-web-dist-build.timer +36 -0
- coord/deploy/coord-web.service +125 -0
- coord/deploy_manifest.py +80 -0
- coord/deploy_units.py +384 -0
- coord/deps.py +115 -0
- coord/diagnose.py +1623 -0
- coord/dispatch.py +1009 -0
- coord/dist_name.py +123 -0
- coord/drive.py +3101 -0
- coord/drive_queue.py +2298 -0
- coord/drive_state.py +870 -0
- coord/events.py +381 -0
- coord/failure_class.py +914 -0
- coord/filelock.py +168 -0
- coord/fleet_config_health.py +300 -0
- coord/freshness.py +206 -0
- coord/gate_a.py +469 -0
- coord/gate_b.py +411 -0
- coord/gate_snapshot.py +385 -0
- coord/gates.py +582 -0
- coord/github_ops.py +1954 -0
- coord/goal.py +125 -0
- coord/graph_health.py +348 -0
- coord/health/__init__.py +69 -0
- coord/health/aggregate.py +129 -0
- coord/health/checks/__init__.py +13 -0
- coord/health/checks/agent_install.py +280 -0
- coord/health/checks/cargo_targets.py +171 -0
- coord/health/checks/claude_binary.py +65 -0
- coord/health/checks/deploy_lane_facts.py +458 -0
- coord/health/checks/disk.py +99 -0
- coord/health/checks/fleet_board.py +89 -0
- coord/health/checks/fleet_deploy_lanes.py +469 -0
- coord/health/checks/fleet_phantom.py +69 -0
- coord/health/checks/fleet_unit_drift.py +151 -0
- coord/health/checks/graph.py +192 -0
- coord/health/checks/plan_usage.py +88 -0
- coord/health/checks/repo_state.py +161 -0
- coord/health/checks/spawned_coord.py +465 -0
- coord/health/checks/timer_active.py +254 -0
- coord/health/checks/toolchain.py +547 -0
- coord/health/checks/unit_drift.py +648 -0
- coord/health/checks/unit_enablement.py +171 -0
- coord/health/checks/worktrees.py +96 -0
- coord/health/cli.py +121 -0
- coord/health/context.py +106 -0
- coord/health/fleet_snapshot.py +477 -0
- coord/health/models.py +250 -0
- coord/health/pypi.py +231 -0
- coord/health/registry.py +240 -0
- coord/health/render.py +82 -0
- coord/health/units.py +60 -0
- coord/hooks.py +106 -0
- coord/housekeeping.py +204 -0
- coord/interactive.py +4286 -0
- coord/issue_store.py +1496 -0
- coord/liveness_auditor.py +293 -0
- coord/machine_pause.py +755 -0
- coord/merge_queue.py +4681 -0
- coord/milestone_chat.py +600 -0
- coord/milestone_dispatch.py +943 -0
- coord/milestone_gate.py +709 -0
- coord/milestone_order.py +840 -0
- coord/mock_author.py +334 -0
- coord/models.py +891 -0
- coord/network.py +269 -0
- coord/new_issue_chat.py +229 -0
- coord/notify.py +3226 -0
- coord/openapi.py +404 -0
- coord/overlap_fence.py +133 -0
- coord/parentage.py +200 -0
- coord/parentage_github.py +58 -0
- coord/pipeline.py +481 -0
- coord/plan_parser.py +266 -0
- coord/plans.py +543 -0
- coord/platform_paths.py +43 -0
- coord/pr_body_lint.py +67 -0
- coord/prereqs.py +533 -0
- coord/progress.py +425 -0
- coord/providers/__init__.py +683 -0
- coord/providers/base.py +218 -0
- coord/providers/claude.py +284 -0
- coord/providers/claude_pty.py +610 -0
- coord/providers/opencode.py +896 -0
- coord/reconcile.py +2233 -0
- coord/refine_chat.py +485 -0
- coord/release_cordon.py +525 -0
- coord/release_propagate.py +1176 -0
- coord/release_verify.py +777 -0
- coord/release_window.py +322 -0
- coord/reports.py +1643 -0
- coord/revalidate.py +1101 -0
- coord/review.py +3317 -0
- coord/scorecard.py +484 -0
- coord/serve_app.py +7192 -0
- coord/skills/update-issue/SKILL.md +93 -0
- coord/smoke.py +1030 -0
- coord/split_work.py +210 -0
- coord/stage_projection.py +650 -0
- coord/state.py +5720 -0
- coord/test_author.py +1064 -0
- coord/test_chat.py +352 -0
- coord/test_orchestrator.py +494 -0
- coord/test_report.py +178 -0
- coord/tui_release.py +271 -0
- coord/usage.py +753 -0
- coord/usage_limits.py +358 -0
- coord/usage_rollup.py +709 -0
- coord/worker_events.py +954 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# coord drive-queue tick — periodic drain of the operator-declared drive
|
|
2
|
+
# queue (#1750/#1754, DQ-4/#1756).
|
|
3
|
+
#
|
|
4
|
+
# A systemd *user* unit (mirrors coord-agent/coord-serve/coord-web/coord-notify),
|
|
5
|
+
# paired with coord-drive-queue.timer. Runs `coord drive-queue tick`, which
|
|
6
|
+
# reconciles the queue against live board state and launches AT MOST ONE
|
|
7
|
+
# `coord drive --tmux` per tick — never more, never past `--max-parallel`.
|
|
8
|
+
# Install ONLY on the box that can see the board (dellserver in production,
|
|
9
|
+
# the same host that runs coord-serve/coord-web/coord-notify): the tick
|
|
10
|
+
# subprocess-launches `coord drive --tmux`, which needs a local tmux server
|
|
11
|
+
# and the repo checkouts under SRC_ROOT, so it belongs on the daemon host, not
|
|
12
|
+
# a thin client.
|
|
13
|
+
#
|
|
14
|
+
# Type=oneshot + `coord drive-queue tick`'s own flock (coord.filelock,
|
|
15
|
+
# drive_queue_lock_path()) means a slow tick cannot stack: if a tick is still
|
|
16
|
+
# running when the timer fires again, the new invocation takes the lock
|
|
17
|
+
# non-blocking, sees it busy, prints "another drive-queue tick is running —
|
|
18
|
+
# skipping", and exits 0 without touching the queue. Nothing here needs to
|
|
19
|
+
# serialize invocations itself.
|
|
20
|
+
#
|
|
21
|
+
# COORD_BIN — the runner's own CLI must be a PINNED, NON-EDITABLE install
|
|
22
|
+
# (#1523): worker branch churn must never be able to rewrite the tool that is
|
|
23
|
+
# driving it, mid-run. On dellserver, ~/.local/bin/coord is a symlink into
|
|
24
|
+
# ~/.coord-venv — the SAME venv deploy/coord-agent.service runs `coord agent`
|
|
25
|
+
# from (see install-agent.sh) — so that already satisfies #1523, and it rides
|
|
26
|
+
# the ordinary agent-venv upgrade lane rather than needing its own. Before
|
|
27
|
+
# relying on a fix landing on this host:
|
|
28
|
+
#
|
|
29
|
+
# coord agent update --machine dellserver # or --all; the standard lane
|
|
30
|
+
# ~/.local/bin/coord --version # VERIFY it took — an upgrade
|
|
31
|
+
# # silently no-ops more often
|
|
32
|
+
# # than you would think
|
|
33
|
+
#
|
|
34
|
+
# Do NOT confuse this with the epic sequencer's separate, bespoke
|
|
35
|
+
# ~/.coord-cli-venv on elitebook (docs/AGENT_OPERATIONS.md's "fourth lane") —
|
|
36
|
+
# that one has no automatic upgrade path at all and was found three releases
|
|
37
|
+
# stale on 2026-07-29. This unit intentionally does not use that pattern.
|
|
38
|
+
#
|
|
39
|
+
# If you ever install this timer on a DIFFERENT machine, first confirm THAT
|
|
40
|
+
# machine's `coord` is a non-editable install before trusting it unattended:
|
|
41
|
+
#
|
|
42
|
+
# readlink -f ~/.local/bin/coord
|
|
43
|
+
# pip show code-coordinator | grep -i editable # must print NOTHING
|
|
44
|
+
#
|
|
45
|
+
# An editable checkout (e.g. elitebook's dev box, where ~/.local/bin/coord
|
|
46
|
+
# points at ~/src/claude-coordinator) lets a worker's branch churn rewrite the
|
|
47
|
+
# runner's own code out from under a live tick. Do not install this unit
|
|
48
|
+
# there. See docs/DRIVE_QUEUE.md.
|
|
49
|
+
#
|
|
50
|
+
# Install:
|
|
51
|
+
# mkdir -p ~/.config/systemd/user
|
|
52
|
+
# cp deploy/coord-drive-queue.service deploy/coord-drive-queue.timer \
|
|
53
|
+
# ~/.config/systemd/user/
|
|
54
|
+
# loginctl enable-linger "$USER" # survive logout / reboot
|
|
55
|
+
# systemctl --user daemon-reload
|
|
56
|
+
# systemctl --user enable --now coord-drive-queue.timer
|
|
57
|
+
#
|
|
58
|
+
# This is a Type=oneshot service activated BY THE TIMER — do not
|
|
59
|
+
# `systemctl --user enable coord-drive-queue.service` directly (it has no
|
|
60
|
+
# [Install] section; the timer unit is what you enable).
|
|
61
|
+
#
|
|
62
|
+
# Prereqs: same as coord-notify — coordinator.yml at ~/.coord/coordinator.yml,
|
|
63
|
+
# a tmux server the launched `coord drive --tmux` sessions can attach to, and
|
|
64
|
+
# the repo checkouts `coord drive` expects under its default SRC_ROOT.
|
|
65
|
+
#
|
|
66
|
+
# See docs/DRIVE_QUEUE.md for the full runbook (enqueue, install, stop,
|
|
67
|
+
# reading QUEUE: STALLED / QUEUE: BLOCKED, and the operational traps).
|
|
68
|
+
#
|
|
69
|
+
# KillMode=process (#1830) — REQUIRED, do not remove. systemd's default
|
|
70
|
+
# KillMode=control-group would reap this unit's entire cgroup when the tick
|
|
71
|
+
# exits. When the tick launches `coord drive --tmux` and NO tmux server is
|
|
72
|
+
# already running, `tmux new-session` has to spawn the server itself, and
|
|
73
|
+
# that server lands inside THIS unit's cgroup — so the drive it just started
|
|
74
|
+
# dies with the tick, seconds later. The failure is invisible whenever a
|
|
75
|
+
# tmux server already exists (e.g. an operator has a terminal open on the
|
|
76
|
+
# box), because then `tmux new-session` hands off to that pre-existing
|
|
77
|
+
# server, which lives outside this cgroup. That made the queue appear to
|
|
78
|
+
# work in every manual/attended test and fail only unattended — see #1830
|
|
79
|
+
# for the full diagnosis and the repro. KillMode=process kills only the
|
|
80
|
+
# tick's own process on unit stop/exit and leaves everything else it
|
|
81
|
+
# started (the tmux server, the drive loop) alone.
|
|
82
|
+
|
|
83
|
+
[Unit]
|
|
84
|
+
Description=coord drive-queue tick (#1756)
|
|
85
|
+
After=network-online.target
|
|
86
|
+
Wants=network-online.target
|
|
87
|
+
|
|
88
|
+
[Service]
|
|
89
|
+
Type=oneshot
|
|
90
|
+
KillMode=process
|
|
91
|
+
# --max-parallel 4 (#2012, raised from 3 in #2057) — the global ceiling,
|
|
92
|
+
# ONE SLOT PER REPO IN THE FLEET: claude-coordinator, quadraui, vimcode,
|
|
93
|
+
# coord-portal. It was 3 because the fleet had three repos when #2012 set
|
|
94
|
+
# it; coord-portal joined afterwards and the number was never revisited, so
|
|
95
|
+
# the fourth repo could never drive concurrently no matter what was queued.
|
|
96
|
+
#
|
|
97
|
+
# KEEP THIS EQUAL TO THE REPO COUNT. It is not a tuning knob for how hard
|
|
98
|
+
# the fleet works — that is what the per-repo ceiling and machine capacity
|
|
99
|
+
# decide. Set it lower and a repo silently loses its lane; set it higher
|
|
100
|
+
# and it does nothing, because the per-repo ceiling still admits only one
|
|
101
|
+
# drive per repo.
|
|
102
|
+
#
|
|
103
|
+
# #1972 added the SECOND ceiling, --max-parallel-per-repo (default 1),
|
|
104
|
+
# which is what keeps two drives off the same repo; without raising this
|
|
105
|
+
# outer one the per-repo ceiling can never bind, because the walk stops at
|
|
106
|
+
# the global limit first and never reaches an entry from a different repo.
|
|
107
|
+
#
|
|
108
|
+
# Raise this ONLY on a host whose coord actually enforces the per-repo
|
|
109
|
+
# ceiling (>= the release carrying #1972). A higher global ceiling on an
|
|
110
|
+
# older coord launches two same-repo drives, which stale each other's
|
|
111
|
+
# Test verdicts (#1479) — the exact collision #1972 exists to prevent.
|
|
112
|
+
ExecStart=%h/.local/bin/coord drive-queue tick --max-parallel 4 --config %h/.coord/coordinator.yml
|
|
113
|
+
# Generous relative to the tick's own ~120s launch-subprocess timeout
|
|
114
|
+
# (_LAUNCH_TIMEOUT_SECONDS in coord/commands/drive_queue.py), but well below
|
|
115
|
+
# the timer's 15-minute cadence (coord-drive-queue.timer) so a genuinely
|
|
116
|
+
# wedged tick can't run into the next fire.
|
|
117
|
+
TimeoutStartSec=300
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# coord drive-queue tick — fires coord-drive-queue.service (Type=oneshot) on
|
|
2
|
+
# a cadence. See that unit's header for the full rationale and the
|
|
3
|
+
# COORD_BIN / #1523 pinned-runner requirement.
|
|
4
|
+
#
|
|
5
|
+
# 3 minutes (was 15). The earlier rationale — "between launches the tick is
|
|
6
|
+
# only ever OBSERVING ... a tighter interval buys nothing but load" — missed
|
|
7
|
+
# that the tick is ALSO the only thing that notices a drive has finished and
|
|
8
|
+
# launches the next entry. Capacity is 1, so the interval is a hard lower
|
|
9
|
+
# bound on the dead time between issues, not idle observation.
|
|
10
|
+
#
|
|
11
|
+
# Measured 2026-08-07 from the queue journal: #1910's PR merged at 12:47:36,
|
|
12
|
+
# the 12:46:42 tick still saw it alive, and the next tick launched #1834 at
|
|
13
|
+
# 13:02:13 — 14m37s of an idle fleet. Completions land uniformly inside the
|
|
14
|
+
# interval, so the expected loss is HALF of it per issue (~7.5min at 15min).
|
|
15
|
+
# Over a queue of 18 that was ~2.2 hours of nothing happening.
|
|
16
|
+
#
|
|
17
|
+
# Reconcile and launch happen in the SAME tick (13:02:12 reconciled #1910
|
|
18
|
+
# done, 13:02:13 launched #1834), so the cost is one interval, not two.
|
|
19
|
+
#
|
|
20
|
+
# A tick is a /board read plus reconciliation, and #1597 added the /board
|
|
21
|
+
# single-flight guard, so 20 ticks/hour is affordable. `Type=oneshot` + the
|
|
22
|
+
# tick's own flock means it cannot stack even if it fired faster than a tick
|
|
23
|
+
# takes to run. AccuracySec is tightened to 15s to match: systemd's default
|
|
24
|
+
# batching slop of 1min is 33% of a 3min interval.
|
|
25
|
+
#
|
|
26
|
+
# Install: see deploy/coord-drive-queue.service's header, or
|
|
27
|
+
# docs/DRIVE_QUEUE.md.
|
|
28
|
+
|
|
29
|
+
[Unit]
|
|
30
|
+
Description=Run coord drive-queue tick every 3 minutes (#1756)
|
|
31
|
+
|
|
32
|
+
[Timer]
|
|
33
|
+
OnBootSec=5min
|
|
34
|
+
OnUnitActiveSec=3min
|
|
35
|
+
AccuracySec=15s
|
|
36
|
+
Persistent=true
|
|
37
|
+
|
|
38
|
+
[Install]
|
|
39
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# coord notify — periodic driver for thin-client setups (#1311).
|
|
2
|
+
#
|
|
3
|
+
# A systemd *user* unit (mirrors coord-agent/coord-serve/coord-web), paired
|
|
4
|
+
# with coord-notify.timer. Runs `coord notify` on a schedule: polls each
|
|
5
|
+
# agent for completion, posts the GH completion/failure comments, and
|
|
6
|
+
# triggers the auto-loop (review-on-completion, fix-on-request-changes,
|
|
7
|
+
# re-review-on-fix-completion). Install ONLY on the box that owns
|
|
8
|
+
# ~/.coord/coord.db (dellserver in production — the same host that runs
|
|
9
|
+
# coord-serve/coord-web) — it must NOT run on a thin-client machine, since
|
|
10
|
+
# `coord notify` there would just re-route itself back over HTTP to the
|
|
11
|
+
# daemon anyway (#906, daemon_reroute_target), needlessly.
|
|
12
|
+
#
|
|
13
|
+
# THIS TIMER IS THE SANCTIONED SINGLE DRIVER for thin-client coord-tui/coord
|
|
14
|
+
# setups. Do NOT also hand-roll a `while`/`watch` loop calling `coord notify`
|
|
15
|
+
# alongside it, and do NOT re-enable the TUI's own 30s auto-notify for a
|
|
16
|
+
# thin client (it is deliberately suppressed — `is_remote_board_service()`,
|
|
17
|
+
# tui/src/app/data.rs). Two drivers racing each other is exactly the failure
|
|
18
|
+
# mode that caused the 2026-06-07 incident (duplicate fix workers from a
|
|
19
|
+
# request-changes verdict getting auto-bounced twice) — see #476/#477 and
|
|
20
|
+
# docs/ARCHITECTURE.md's "no orchestration daemon" section.
|
|
21
|
+
#
|
|
22
|
+
# Install:
|
|
23
|
+
# mkdir -p ~/.config/systemd/user
|
|
24
|
+
# cp deploy/coord-notify.service deploy/coord-notify.timer ~/.config/systemd/user/
|
|
25
|
+
# loginctl enable-linger "$USER" # survive logout / reboot
|
|
26
|
+
# systemctl --user daemon-reload
|
|
27
|
+
# systemctl --user enable --now coord-notify.timer
|
|
28
|
+
#
|
|
29
|
+
# This is a Type=oneshot service activated BY THE TIMER — do not
|
|
30
|
+
# `systemctl --user enable coord-notify.service` directly (it has no
|
|
31
|
+
# [Install] section; the timer unit is what you enable).
|
|
32
|
+
#
|
|
33
|
+
# Prereqs: same as coord-serve (coordinator.yml at ~/.coord/coordinator.yml,
|
|
34
|
+
# a `gh`-authenticated environment for the GitHub writes `coord notify` makes).
|
|
35
|
+
#
|
|
36
|
+
# See docs/AGENT_OPERATIONS.md → "Periodic coord notify (coord-notify timer)".
|
|
37
|
+
|
|
38
|
+
[Unit]
|
|
39
|
+
Description=coord notify — periodic pipeline driver (#1311)
|
|
40
|
+
After=network-online.target
|
|
41
|
+
Wants=network-online.target
|
|
42
|
+
|
|
43
|
+
[Service]
|
|
44
|
+
Type=oneshot
|
|
45
|
+
ExecStart=%h/.coord-venv/bin/coord notify --config %h/.coord/coordinator.yml
|
|
46
|
+
# Bounded below the timer's 5-minute cadence (coord-notify.timer) so a slow
|
|
47
|
+
# run can't overlap the next fire.
|
|
48
|
+
TimeoutStartSec=280
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coord notify — periodic driver for thin-client setups (#1311). Fires
|
|
2
|
+
# coord-notify.service (Type=oneshot) on a cadence. See that unit's header
|
|
3
|
+
# for the full rationale ("THIS TIMER IS THE SANCTIONED SINGLE DRIVER").
|
|
4
|
+
#
|
|
5
|
+
# 5 minutes is deliberately coarser than the in-process TUI's 30s cadence —
|
|
6
|
+
# this timer's job is "don't let the pipeline stall unnoticed for the whole
|
|
7
|
+
# session," not sub-minute responsiveness. Tighten OnUnitActiveSec if that
|
|
8
|
+
# proves too slow in practice; there's no correctness reason it needs to be
|
|
9
|
+
# this conservative, just no reason to hammer `gh` harder than necessary.
|
|
10
|
+
#
|
|
11
|
+
# Install: see deploy/coord-notify.service's header, or
|
|
12
|
+
# docs/AGENT_OPERATIONS.md → "Periodic coord notify (coord-notify timer)".
|
|
13
|
+
|
|
14
|
+
[Unit]
|
|
15
|
+
Description=Run coord notify every few minutes (#1311)
|
|
16
|
+
|
|
17
|
+
[Timer]
|
|
18
|
+
OnBootSec=2min
|
|
19
|
+
OnUnitActiveSec=5min
|
|
20
|
+
AccuracySec=30s
|
|
21
|
+
Persistent=true
|
|
22
|
+
|
|
23
|
+
[Install]
|
|
24
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# coord release propagate — the PROPAGATE half of merge-triggered release
|
|
2
|
+
# (#1835, PKG-7).
|
|
3
|
+
#
|
|
4
|
+
# A systemd *user* unit (mirrors coord-drive-queue), paired with
|
|
5
|
+
# coord-release-propagate.timer. Each fire is one attempt:
|
|
6
|
+
#
|
|
7
|
+
# 1. resolve the released version from PyPI's simple index;
|
|
8
|
+
# 2. ask the drive queue and the board whether the fleet is QUIESCENT;
|
|
9
|
+
# 3. if it is not — record a deferral and exit 0. This is the normal,
|
|
10
|
+
# expected outcome most of the time and it is NOT a failure;
|
|
11
|
+
# 4. if it is — roll each lane in the daemon-first order, run
|
|
12
|
+
# `coord release verify` as the final gate, roll back on red, and
|
|
13
|
+
# release the drive-queue deploy gates (#1757) that were waiting for
|
|
14
|
+
# exactly this deploy.
|
|
15
|
+
#
|
|
16
|
+
# WHY THIS IS A TIMER AND NOT A GITHUB ACTION
|
|
17
|
+
# -------------------------------------------
|
|
18
|
+
# The *publish* half IS a GitHub Action (.github/workflows/auto-release.yml)
|
|
19
|
+
# because publishing touches no running host. Propagation restarts every
|
|
20
|
+
# agent, and a restart kills every in-flight headless worker — so it must
|
|
21
|
+
# wait for a window where nothing is mid-flight. GitHub cannot see that;
|
|
22
|
+
# only the board can. Hence: publish on merge, propagate on quiescence.
|
|
23
|
+
#
|
|
24
|
+
# WHICH HOST
|
|
25
|
+
# ----------
|
|
26
|
+
# Install ONLY on the daemon host (dellserver in production — the same box
|
|
27
|
+
# that runs coord-serve / coord-drive-queue / coord-notify). This unit reads
|
|
28
|
+
# `/board` and POSTs to every agent, and it releases drive-queue holds, so it
|
|
29
|
+
# belongs where the queue and the board already live. Installing a second
|
|
30
|
+
# copy elsewhere gives the fleet two independent things deciding to restart
|
|
31
|
+
# it, which is the #1440 two-overseers failure in a new costume.
|
|
32
|
+
#
|
|
33
|
+
# COORD_BIN — same #1523 requirement as coord-drive-queue.service: the CLI
|
|
34
|
+
# this unit runs must be a PINNED, NON-EDITABLE install, because it is the
|
|
35
|
+
# thing that upgrades everything else. On dellserver ~/.local/bin/coord is a
|
|
36
|
+
# symlink into ~/.coord-venv, which satisfies that and rides the ordinary
|
|
37
|
+
# agent-venv lane. Do NOT install this unit on a dev box whose
|
|
38
|
+
# ~/.local/bin/coord points at a checkout.
|
|
39
|
+
#
|
|
40
|
+
# SELF-UPDATE ORDERING: this unit's own CLI lives in the venv it is about to
|
|
41
|
+
# swap. That is safe *because* #1241 made the swap atomic (blue/green symlink
|
|
42
|
+
# flip) — a running `coord release propagate` keeps executing the fully-old
|
|
43
|
+
# slot it started from; it can never observe a half-written site-packages.
|
|
44
|
+
# The next fire runs the new one.
|
|
45
|
+
#
|
|
46
|
+
# Install:
|
|
47
|
+
# mkdir -p ~/.config/systemd/user
|
|
48
|
+
# cp deploy/coord-release-propagate.service \
|
|
49
|
+
# deploy/coord-release-propagate.timer ~/.config/systemd/user/
|
|
50
|
+
# loginctl enable-linger "$USER"
|
|
51
|
+
# systemctl --user daemon-reload
|
|
52
|
+
# systemctl --user enable --now coord-release-propagate.timer
|
|
53
|
+
#
|
|
54
|
+
# (After the first release carrying #1835, `coord release propagate` installs
|
|
55
|
+
# unit updates itself via each agent's POST /deploy-units — but it only
|
|
56
|
+
# refreshes units a host ALREADY has, so this first install is by hand, once.)
|
|
57
|
+
#
|
|
58
|
+
# This is a Type=oneshot service activated BY THE TIMER — do not
|
|
59
|
+
# `systemctl --user enable coord-release-propagate.service` directly.
|
|
60
|
+
#
|
|
61
|
+
# Read the journal it writes with `coord release history`, not just
|
|
62
|
+
# `journalctl`: the JSONL record survives a half-installed venv and is the
|
|
63
|
+
# thing that makes "the fleet reached that version" checkable after the fact.
|
|
64
|
+
|
|
65
|
+
[Unit]
|
|
66
|
+
Description=Propagate the released version to the fleet at the next quiescent window (#1835)
|
|
67
|
+
After=network-online.target
|
|
68
|
+
Wants=network-online.target
|
|
69
|
+
|
|
70
|
+
[Service]
|
|
71
|
+
Type=oneshot
|
|
72
|
+
# No --force, ever, from a timer. --force rolls over a BUSY fleet and kills
|
|
73
|
+
# in-flight headless workers — the single thing this whole design exists to
|
|
74
|
+
# avoid. It stays an interactive-operator flag.
|
|
75
|
+
#
|
|
76
|
+
# --verify and --rollback-on-red are the defaults; named explicitly here so
|
|
77
|
+
# the unit reads as the policy it is, and so a future default change cannot
|
|
78
|
+
# silently disarm the fleet's rollback.
|
|
79
|
+
ExecStart=%h/.local/bin/coord release propagate --verify --rollback-on-red --config %h/.coord/coordinator.yml
|
|
80
|
+
# A roll waits up to --timeout (180s) per agent for the new version to be
|
|
81
|
+
# reported, across every machine, plus two full verification sweeps. Generous,
|
|
82
|
+
# but well under the timer's cadence so a wedged run cannot run into the next.
|
|
83
|
+
TimeoutStartSec=1800
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# coord release propagate — fires coord-release-propagate.service
|
|
2
|
+
# (Type=oneshot) on a cadence. See that unit's header for the full rationale.
|
|
3
|
+
#
|
|
4
|
+
# 20 minutes. The tick is cheap and almost always a no-op: it reads the board,
|
|
5
|
+
# finds the fleet busy, records a deferral and exits. What the interval
|
|
6
|
+
# actually buys is *latency from quiescence to deployed* — the window between
|
|
7
|
+
# a drive finishing and the next one launching is minutes wide
|
|
8
|
+
# (coord-drive-queue.timer fires every 3 minutes), so a slow propagation
|
|
9
|
+
# cadence would routinely miss the gap and the fleet would sit a release
|
|
10
|
+
# behind for hours.
|
|
11
|
+
#
|
|
12
|
+
# Not tighter than that, for two reasons. Each fire that *does* find a window
|
|
13
|
+
# restarts every agent, so an over-eager cadence turns a transient idle moment
|
|
14
|
+
# into a fleet restart the operator did not ask for. And a propagation run
|
|
15
|
+
# holds the window it found: an interval shorter than a full roll+verify
|
|
16
|
+
# sweep would just queue up runs behind each other.
|
|
17
|
+
#
|
|
18
|
+
# Persistent=true so a host that was asleep or rebooted catches up on the next
|
|
19
|
+
# boot rather than waiting a full interval — the whole point is that the fleet
|
|
20
|
+
# converges on the released version without anyone thinking about it.
|
|
21
|
+
#
|
|
22
|
+
# RandomizedDelaySec spreads fires if this unit is ever (against the advice in
|
|
23
|
+
# the service header) installed on more than one host: two propagations
|
|
24
|
+
# racing would both find the same window and both start restarting agents.
|
|
25
|
+
# The real defence is "install on the daemon host only"; this is the belt.
|
|
26
|
+
|
|
27
|
+
[Unit]
|
|
28
|
+
Description=Attempt fleet propagation of the released version every 20 minutes (#1835)
|
|
29
|
+
|
|
30
|
+
[Timer]
|
|
31
|
+
OnBootSec=10min
|
|
32
|
+
OnUnitActiveSec=20min
|
|
33
|
+
RandomizedDelaySec=2min
|
|
34
|
+
AccuracySec=1min
|
|
35
|
+
Persistent=true
|
|
36
|
+
|
|
37
|
+
[Install]
|
|
38
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# coord release nightly-window — guarantee the daemon host rolls instead of
|
|
2
|
+
# waiting for a quiescent window that may never come (#2112).
|
|
3
|
+
#
|
|
4
|
+
# A systemd *user* unit (mirrors coord-drive-queue/coord-release-propagate),
|
|
5
|
+
# paired with coord-release-window.timer. Each fire is one attempt:
|
|
6
|
+
#
|
|
7
|
+
# 1. resolve the released version and the daemon host's own python-lane
|
|
8
|
+
# version; if the daemon host is already current, STOP — the queue is
|
|
9
|
+
# never touched (#2112 acceptance 3);
|
|
10
|
+
# 2. otherwise, `systemctl --user stop coord-drive-queue.timer` — no new
|
|
11
|
+
# drives may launch for the rest of this run;
|
|
12
|
+
# 3. wait (bounded — see TimeoutStartSec below and
|
|
13
|
+
# `coord release nightly-window --drain-deadline`) for whatever is
|
|
14
|
+
# already running to finish, reconciling with `coord drive-queue tick
|
|
15
|
+
# --reconcile-only` on every poll (#2110 — this is what stops the
|
|
16
|
+
# drain from deadlocking now that the timer that normally does that
|
|
17
|
+
# reconciliation is stopped);
|
|
18
|
+
# 4. roll with `coord release propagate --daemon-host <derived>
|
|
19
|
+
# --target <resolved>` — no --force, ever (trap 1);
|
|
20
|
+
# 5. `systemctl --user start coord-drive-queue.timer` — ALWAYS, whatever
|
|
21
|
+
# happened in 1-4, including a crash;
|
|
22
|
+
# 6. report the outcome: stdout (this unit's journal), the
|
|
23
|
+
# `~/.coord/release_window.jsonl` journal (`coord release
|
|
24
|
+
# window-history`), and — for anything short of a clean roll or a
|
|
25
|
+
# confirmed already-current fleet — the same escalation channel
|
|
26
|
+
# `coord drive escalations` and the TUI already surface #2101's
|
|
27
|
+
# drain-deadline escalation through.
|
|
28
|
+
#
|
|
29
|
+
# WHY THIS EXISTS SEPARATELY FROM coord-release-propagate.timer
|
|
30
|
+
# ----------------------------------------------------------------
|
|
31
|
+
# `coord release propagate` already rolls every OTHER host at its own
|
|
32
|
+
# per-host quiescent window (#2067), and cordons a behind host so it drains
|
|
33
|
+
# on its own (#2101). Neither mechanism reaches the daemon host itself: the
|
|
34
|
+
# daemon must lead every roll (the documented 405), so if IT is busy the
|
|
35
|
+
# whole run defers — and dellserver is both the daemon host and the box
|
|
36
|
+
# whose drive-queue tick launches work fleet-wide, so almost any queued
|
|
37
|
+
# drive anywhere keeps it "busy". Cordoning the daemon host would not help:
|
|
38
|
+
# cordoning stops new work from ROUTING there, but does not stop the
|
|
39
|
+
# daemon's own tick from LAUNCHING new drive-queue entries in the first
|
|
40
|
+
# place. The only way to guarantee the daemon host reaches quiescence is to
|
|
41
|
+
# stop the thing that launches work onto it — which is exactly what this
|
|
42
|
+
# unit's `--ensure-queue-running` ExecStopPost= below makes safe to do
|
|
43
|
+
# unattended. See coord/release_window.py's module docstring for the full
|
|
44
|
+
# mechanism and the 2026-08-10 measurement (eleven releases behind, with
|
|
45
|
+
# elitebook idle and rollable the whole time).
|
|
46
|
+
#
|
|
47
|
+
# GATED ON #2110 — HARD PREREQUISITE. Steps 2-3 above are exactly the
|
|
48
|
+
# sequence that deadlocked on 2026-08-10: the reconciler that clears a
|
|
49
|
+
# finished drive's `running` row lives INSIDE `coord drive-queue tick`, so
|
|
50
|
+
# stopping the timer used to stop reconciliation too, and the last drive's
|
|
51
|
+
# row would stay `running` forever — the daemon host would read as busy
|
|
52
|
+
# permanently and this unit would defer every night, unattended, silently,
|
|
53
|
+
# forever. Do not install this unit on a build that predates #2110.
|
|
54
|
+
#
|
|
55
|
+
# WHICH HOST
|
|
56
|
+
# ----------
|
|
57
|
+
# Install ONLY on the daemon host (dellserver in production — the same box
|
|
58
|
+
# that runs coord-serve/coord-drive-queue/coord-release-propagate). This
|
|
59
|
+
# unit stops and restarts coord-drive-queue.timer, which only exists on
|
|
60
|
+
# that host; installing a second copy elsewhere has no queue to stop and
|
|
61
|
+
# would just repeatedly fail `systemctl --user stop coord-drive-queue.timer`
|
|
62
|
+
# — a locally-scoped, harmless-but-noisy failure, not the #1440
|
|
63
|
+
# two-overseers hazard `coord-release-propagate.timer`'s header warns about,
|
|
64
|
+
# but still pointless. Don't.
|
|
65
|
+
#
|
|
66
|
+
# COORD_BIN — same #1523 pinned-non-editable-install requirement as
|
|
67
|
+
# coord-drive-queue.service/coord-release-propagate.service: the CLI this
|
|
68
|
+
# unit runs must not be rewritable out from under a live run. See those
|
|
69
|
+
# units' headers for the full rationale; on dellserver ~/.local/bin/coord
|
|
70
|
+
# already satisfies it.
|
|
71
|
+
#
|
|
72
|
+
# Install:
|
|
73
|
+
# mkdir -p ~/.config/systemd/user
|
|
74
|
+
# cp deploy/coord-release-window.service \
|
|
75
|
+
# deploy/coord-release-window.timer ~/.config/systemd/user/
|
|
76
|
+
# loginctl enable-linger "$USER"
|
|
77
|
+
# systemctl --user daemon-reload
|
|
78
|
+
# systemctl --user enable --now coord-release-window.timer
|
|
79
|
+
#
|
|
80
|
+
# Read the journal it writes with `coord release window-history`, not just
|
|
81
|
+
# `journalctl` — the JSONL record survives a half-installed venv, same
|
|
82
|
+
# reason coord-release-propagate.service's does.
|
|
83
|
+
|
|
84
|
+
[Unit]
|
|
85
|
+
Description=Nightly release window on the daemon host (#2112)
|
|
86
|
+
After=network-online.target coord-drive-queue.service
|
|
87
|
+
Wants=network-online.target
|
|
88
|
+
|
|
89
|
+
[Service]
|
|
90
|
+
Type=oneshot
|
|
91
|
+
# No --force, ever (trap 1) — coord/commands/release.py's
|
|
92
|
+
# release_nightly_window never passes it to the `coord release propagate`
|
|
93
|
+
# it shells out to. A drain that has not finished by the deadline is
|
|
94
|
+
# reported and declined, not overridden.
|
|
95
|
+
ExecStart=%h/.local/bin/coord release nightly-window --config %h/.coord/coordinator.yml
|
|
96
|
+
# #2112 trap 4 / acceptance 4: "the queue timer is running after the job in
|
|
97
|
+
# EVERY path, including the failure ones — test by killing the job
|
|
98
|
+
# mid-window." A `finally` block inside ExecStart cannot survive SIGKILL
|
|
99
|
+
# (TimeoutStartSec below, or an operator's own `systemctl --user kill`).
|
|
100
|
+
# ExecStopPost= is systemd's OWN guarantee of exactly that: it runs after
|
|
101
|
+
# the main process exits for ANY reason — clean exit, non-zero exit, or
|
|
102
|
+
# killed by a signal — so this is the backstop the in-process `finally`
|
|
103
|
+
# cannot be. `--ensure-queue-running` does nothing but
|
|
104
|
+
# `systemctl --user start coord-drive-queue.timer` and is idempotent: on
|
|
105
|
+
# the overwhelmingly common path where ExecStart already restarted the
|
|
106
|
+
# timer cleanly, this is a harmless no-op assertion, not a second action.
|
|
107
|
+
ExecStopPost=%h/.local/bin/coord release nightly-window --ensure-queue-running --config %h/.coord/coordinator.yml
|
|
108
|
+
# The drain alone may wait up to its own --drain-deadline (default 1h) on
|
|
109
|
+
# top of the roll+verify sweep `coord release propagate` performs
|
|
110
|
+
# (generous relative to its own 1800s TimeoutStartSec — see
|
|
111
|
+
# coord-release-propagate.service). 2.5h total leaves this unit's own
|
|
112
|
+
# worst case well inside the 22:00-08:00 quiet-hours window even starting
|
|
113
|
+
# at 03:00, per coord-release-window.timer's header.
|
|
114
|
+
TimeoutStartSec=9000
|
|
115
|
+
|
|
116
|
+
# This is a Type=oneshot service activated BY THE TIMER — do not
|
|
117
|
+
# `systemctl --user enable coord-release-window.service` directly (it has
|
|
118
|
+
# no [Install] section; the timer unit is what you enable — same pattern
|
|
119
|
+
# as coord-release-propagate.service/coord-drive-queue.service).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# coord release nightly-window — fires coord-release-window.service
|
|
2
|
+
# (Type=oneshot) once a night. See that unit's header for the full
|
|
3
|
+
# rationale (#2112).
|
|
4
|
+
#
|
|
5
|
+
# 03:00 local, inside the 22:00-08:00 quiet hours already used for
|
|
6
|
+
# notifications (see coord-notify's own schedule) — an operator is least
|
|
7
|
+
# likely to be mid-drive at 03:00, and the drain deadline
|
|
8
|
+
# (coord/release_window.py's DEFAULT_DRAIN_DEADLINE_SECONDS, 1h by default)
|
|
9
|
+
# leaves comfortable room to finish and restart the queue timer well before
|
|
10
|
+
# the working day, even in the worst case where the drain times out.
|
|
11
|
+
#
|
|
12
|
+
# RandomizedDelaySec is small and exists only to avoid firing at the exact
|
|
13
|
+
# same wall-clock instant `coord-drive-queue.timer`'s own OnCalendar-free
|
|
14
|
+
# interval schedule might land on, not because two hosts might race this
|
|
15
|
+
# unit — it belongs on the daemon host ONLY (same rule as
|
|
16
|
+
# coord-release-propagate.timer; see that unit's header for why a second
|
|
17
|
+
# copy elsewhere is the #1440 two-overseers failure in a new costume).
|
|
18
|
+
#
|
|
19
|
+
# Persistent=true (#2112 trap 4 / acceptance-adjacent): a host that was
|
|
20
|
+
# asleep or rebooted through 03:00 still catches up on the next boot, rather
|
|
21
|
+
# than silently skipping a whole night. `coord release nightly-window`
|
|
22
|
+
# itself is idempotent — see its module docstring's `needs_roll`: a fleet
|
|
23
|
+
# already current at whatever time this actually fires does not stop the
|
|
24
|
+
# queue at all.
|
|
25
|
+
|
|
26
|
+
[Unit]
|
|
27
|
+
Description=Nightly release window on the daemon host (#2112)
|
|
28
|
+
|
|
29
|
+
[Timer]
|
|
30
|
+
OnCalendar=*-*-* 03:00:00
|
|
31
|
+
RandomizedDelaySec=5min
|
|
32
|
+
AccuracySec=1min
|
|
33
|
+
Persistent=true
|
|
34
|
+
|
|
35
|
+
[Install]
|
|
36
|
+
WantedBy=timers.target
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# coord serve — portable control-center daemon (#584/#591).
|
|
2
|
+
#
|
|
3
|
+
# A systemd *user* unit (mirrors coord-agent). Runs the read+write daemon that
|
|
4
|
+
# fronts the one shared ~/.coord/coord.db so any Tailscale machine renders/drives
|
|
5
|
+
# the same board. Install on the always-on box that owns the DB (dellserver).
|
|
6
|
+
#
|
|
7
|
+
# Install:
|
|
8
|
+
# mkdir -p ~/.config/systemd/user
|
|
9
|
+
# cp deploy/coord-serve.service ~/.config/systemd/user/
|
|
10
|
+
# loginctl enable-linger "$USER" # survive logout / reboot
|
|
11
|
+
# systemctl --user daemon-reload
|
|
12
|
+
# systemctl --user enable --now coord-serve
|
|
13
|
+
#
|
|
14
|
+
# Prereqs on the daemon host:
|
|
15
|
+
# - ~/.coord-venv has a coord BUILD that includes `coord serve` (a release
|
|
16
|
+
# >= the #584/#590 cut, or an editable checkout of the branch). PyPI
|
|
17
|
+
# releases before that do NOT have the command.
|
|
18
|
+
# - ~/.coord/coordinator.yml exists (canonical location; the daemon serves it
|
|
19
|
+
# at GET /config so clients need none). Resolution: $COORD_CONFIG →
|
|
20
|
+
# ~/.coord/coordinator.yml → ./coordinator.yml.
|
|
21
|
+
# - ~/.coord/coord.db exists (after the one-time cutover/ETL).
|
|
22
|
+
# - Bearer token (recommended): printf '%s' "<secret>" > ~/.coord/serve_token
|
|
23
|
+
# && chmod 600 ~/.coord/serve_token (resolve_serve_token reads it; never
|
|
24
|
+
# put --token on ExecStart — it would leak via `ps`).
|
|
25
|
+
#
|
|
26
|
+
# See docs/AGENT_OPERATIONS.md → "Control-center daemon (coord serve)".
|
|
27
|
+
|
|
28
|
+
[Unit]
|
|
29
|
+
Description=coord serve — portable control-center daemon (#584)
|
|
30
|
+
After=network-online.target
|
|
31
|
+
Wants=network-online.target
|
|
32
|
+
|
|
33
|
+
[Service]
|
|
34
|
+
Type=simple
|
|
35
|
+
# PATH here merges TWO independent, still-load-bearing fixes — #1831 found
|
|
36
|
+
# them landed a month apart, each unaware of the other, and each one alone
|
|
37
|
+
# regresses the other's issue:
|
|
38
|
+
#
|
|
39
|
+
# - #1814 added ~/.cargo/bin. Defence in depth ONLY — never the fix on its
|
|
40
|
+
# own. A systemd user unit's PATH is systemd's, not a login shell's:
|
|
41
|
+
# ~/.profile is never sourced, so ~/.cargo/bin is absent and `cargo test`
|
|
42
|
+
# inside a `coord merge --revalidate` run died with "command not found".
|
|
43
|
+
# The real fix is in scripts/coord-test-runner.sh, which resolves each
|
|
44
|
+
# toolchain explicitly and reports a MISSING one as an infrastructure
|
|
45
|
+
# error rather than a red suite — because a PATH= line here fixes this
|
|
46
|
+
# host and leaves the next one broken silently, which is the same failure
|
|
47
|
+
# shape as the bug. Keep both.
|
|
48
|
+
# - #1117 added the repo venv (%h/src/claude-coordinator/.venv/bin) so
|
|
49
|
+
# acceptance.drivers subprocesses shelled out via
|
|
50
|
+
# subprocess.run(shell=True) (e.g. the cli-pytest driver's `pytest
|
|
51
|
+
# tests/acceptance/{ms}`) resolve `pytest`/`python3` with the repo's
|
|
52
|
+
# runtime deps (e.g. httpx) instead of falling back to bare
|
|
53
|
+
# /usr/bin/python3, which lacks them -> every acceptance run/record
|
|
54
|
+
# silently failed with an ImportError, not a real test failure.
|
|
55
|
+
#
|
|
56
|
+
# #1831: putting the repo venv FIRST let a stale editable checkout there
|
|
57
|
+
# shadow the pinned release for every `shutil.which("coord")` resolution
|
|
58
|
+
# (coord_argv(), coord/drive.py) — the daemon ran 0.4.105 while everything
|
|
59
|
+
# it spawned ran 0.4.103 editable code from a checkout nobody was updating.
|
|
60
|
+
# ~/.local/bin goes ahead of it (a symlink to ~/.coord-venv/bin/coord, i.e.
|
|
61
|
+
# the pinned release) so `coord` always resolves there first and a stale
|
|
62
|
+
# checkout can never shadow it again; the repo venv stays LAST, after
|
|
63
|
+
# ~/.local/bin, so pytest/python3 still see it for #1117. This ordering is
|
|
64
|
+
# the structural fix — see the `coord health`/`coord doctor` unit_drift
|
|
65
|
+
# check (#1831), which flags any unit whose PATH puts a `.venv/bin` entry
|
|
66
|
+
# ahead of ~/.local/bin or ~/.coord-venv/bin.
|
|
67
|
+
Environment=PATH=%h/.cargo/bin:%h/.local/bin:%h/src/claude-coordinator/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
|
|
68
|
+
ExecStart=%h/.coord-venv/bin/coord serve --config %h/.coord/coordinator.yml --host 0.0.0.0 --port 7435
|
|
69
|
+
Restart=on-failure
|
|
70
|
+
RestartSec=3
|
|
71
|
+
# #2095: coord-web (its sibling on the same host, restarted through the same
|
|
72
|
+
# `_restart_sibling_unit` caller) went down for the length of a fleet roll
|
|
73
|
+
# because nothing bounded how long systemd would wait on its stop — an SSE
|
|
74
|
+
# client held it open indefinitely. coord-serve serves no `text/event-stream`
|
|
75
|
+
# endpoint today so it was not the unit that actually hung, but nothing
|
|
76
|
+
# structural stops a future one (a stuck DB write, a wedged client
|
|
77
|
+
# connection) from doing the same thing here, uncapped, for up to systemd's
|
|
78
|
+
# 90s default. Bounding it explicitly costs nothing on the fast path.
|
|
79
|
+
TimeoutStopSec=10
|
|
80
|
+
|
|
81
|
+
[Install]
|
|
82
|
+
WantedBy=default.target
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# coord-web-dist-build — pull merged main and rebuild the React webapp bundle
|
|
2
|
+
# that `coord web --dist` serves (#1543), paired with
|
|
3
|
+
# coord-web-dist-build.timer.
|
|
4
|
+
#
|
|
5
|
+
# A systemd *user* unit, Type=oneshot, mirroring coord-drive-queue.service's
|
|
6
|
+
# tick pattern (timer-fired, its own flock so a slow run can't stack). Runs
|
|
7
|
+
# deploy/coord-web-dist-build.sh, which:
|
|
8
|
+
# - fetches origin/main into ~/src/claude-coordinator (read-only — only
|
|
9
|
+
# `git fetch` + `rev-parse`, never touches that checkout's working tree
|
|
10
|
+
# or whatever branch an operator has it parked on),
|
|
11
|
+
# - builds coord/dashboard/webapp from that SHA inside a DEDICATED worktree
|
|
12
|
+
# (~/.coord-web-checkout, never shared with an operator or `coord drive`),
|
|
13
|
+
# - atomically repoints ~/coord-web-dist at the new build.
|
|
14
|
+
#
|
|
15
|
+
# Deliberately separate from coord-agent/coord-serve/coord-web's
|
|
16
|
+
# ~/.coord-venv: this unit never runs `pip install`, never touches that venv,
|
|
17
|
+
# and its failure mode (a broken npm build) leaves the PREVIOUS release live
|
|
18
|
+
# — see the script's header for the exact guarantee. Restarting THIS unit
|
|
19
|
+
# does not restart coord-web, coord-serve, or coord-agent, and does not
|
|
20
|
+
# interrupt an attended /ws/terminal session (no process restart happens at
|
|
21
|
+
# all on a normal publish — see the script header).
|
|
22
|
+
#
|
|
23
|
+
# Install: see deploy/coord-web-dist-build.sh's header, or
|
|
24
|
+
# docs/PHONE_WEBAPP.md / docs/AGENT_OPERATIONS.md.
|
|
25
|
+
#
|
|
26
|
+
# This is a Type=oneshot service activated BY THE TIMER — do not
|
|
27
|
+
# `systemctl --user enable coord-web-dist-build.service` directly (it has no
|
|
28
|
+
# [Install] section; the timer unit is what you enable).
|
|
29
|
+
|
|
30
|
+
[Unit]
|
|
31
|
+
Description=Build coord webapp dist from merged main (#1543)
|
|
32
|
+
After=network-online.target
|
|
33
|
+
Wants=network-online.target
|
|
34
|
+
|
|
35
|
+
[Service]
|
|
36
|
+
Type=oneshot
|
|
37
|
+
ExecStart=%h/.local/bin/coord-web-dist-build.sh
|
|
38
|
+
# The script has its own flock (coord-web-dist-build.lock); this is a
|
|
39
|
+
# generous ceiling for a wedged `npm ci`/`npm run build`, well below the
|
|
40
|
+
# timer's 1-minute cadence so a genuinely stuck run can't run into the next
|
|
41
|
+
# fire indefinitely.
|
|
42
|
+
TimeoutStartSec=300
|
|
43
|
+
Environment=PATH=%h/.local/bin:%h/.cargo/bin:/usr/local/bin:/usr/bin:/bin
|