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
coord/mock_author.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""#930 (docs/ORACLE_LOOP.md, Gate A): dispatch a `type="mock-author"` agent.
|
|
2
|
+
|
|
3
|
+
Gate A is the milestone's pre-work architecture gate: before any of a
|
|
4
|
+
milestone's issues may dispatch, an independent agent — zero shared context
|
|
5
|
+
with the eventual workers, mirroring the adversarial code reviewer — renders
|
|
6
|
+
a viewable mock of the milestone's user-facing surface and pins the exact
|
|
7
|
+
black-box contract (CLI names, key screen text, API field shapes) that both
|
|
8
|
+
the workers and the independent test-author (#931) implement/test to.
|
|
9
|
+
|
|
10
|
+
Used by `coord acceptance mock <repo> <tracking_issue>` (the CLI command in
|
|
11
|
+
``coord/commands/acceptance.py``). Deliberately keyed by the milestone's
|
|
12
|
+
tracking-issue number, not a `--milestone NN` flag — every sibling milestone
|
|
13
|
+
command (`coord milestone order/dispatch/chat/gate-c`) takes
|
|
14
|
+
`<repo> <tracking_issue>`, and the milestone number itself is resolved from
|
|
15
|
+
the tracking issue, same as `coord/milestone_chat.py` already does. The
|
|
16
|
+
on-disk artifact directory is still `tests/acceptance/ms-<milestone_number>/`
|
|
17
|
+
per ORACLE_LOOP.md's settled layout.
|
|
18
|
+
|
|
19
|
+
The mock-author gets a real git worktree + branch (unlike the read-only
|
|
20
|
+
`milestone-chat` type) since its whole job is committing files under
|
|
21
|
+
`tests/acceptance/ms-NN/` — see `type="mock-author"` handling in
|
|
22
|
+
``coord/agent.py`` (``MOCK_AUTHOR_SYSTEM_PROMPT`` / ``WRITE_CAPABLE_SPEC_
|
|
23
|
+
TYPES``) and the matching exemption from the acceptance-dir auto-forbid in
|
|
24
|
+
``coord/dispatch.py``. It dispatches through the same
|
|
25
|
+
Work → Test → Review → Merge pipeline as any other branch (`required_gates`
|
|
26
|
+
is the repo's normal `default_gates`) — Gate A produces a normal reviewed
|
|
27
|
+
commit, not a special-cased one.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import uuid
|
|
32
|
+
|
|
33
|
+
import httpx
|
|
34
|
+
|
|
35
|
+
from coord import github_ops
|
|
36
|
+
from coord.acceptance import ms_dirname
|
|
37
|
+
from coord.claim import claim_message, find_work_claim
|
|
38
|
+
from coord.config import Config
|
|
39
|
+
from coord.milestone_chat import _fetch_milestone_issues
|
|
40
|
+
from coord.milestone_dispatch import pick_machine
|
|
41
|
+
from coord.models import Machine, Proposal
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def build_mock_author_briefing(
|
|
45
|
+
*,
|
|
46
|
+
repo_slug: str,
|
|
47
|
+
milestone_title: str,
|
|
48
|
+
milestone_number: int,
|
|
49
|
+
tracking_issue_number: int,
|
|
50
|
+
tracking_issue_body: str,
|
|
51
|
+
issues: list[dict],
|
|
52
|
+
driver_kind: str,
|
|
53
|
+
driver_mock_glob: str,
|
|
54
|
+
) -> str:
|
|
55
|
+
"""Compose the seed briefing the mock-author sees as its first user
|
|
56
|
+
message: milestone context + exactly where its output must land."""
|
|
57
|
+
ms_dir = f"tests/acceptance/{ms_dirname(milestone_number)}"
|
|
58
|
+
|
|
59
|
+
parts: list[str] = []
|
|
60
|
+
parts.append(
|
|
61
|
+
f"=== Gate A mock-author context for {repo_slug} "
|
|
62
|
+
f"— milestone {milestone_title!r} ===\n"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
parts.append(f"TRACKING ISSUE: #{tracking_issue_number}")
|
|
66
|
+
parts.append("MILESTONE TRACKING ISSUE BODY:")
|
|
67
|
+
parts.append(tracking_issue_body.strip() if tracking_issue_body.strip() else "(empty)")
|
|
68
|
+
parts.append("")
|
|
69
|
+
|
|
70
|
+
parts.append(f"OPEN ISSUES UNDER THIS MILESTONE ({len(issues)}):")
|
|
71
|
+
if issues:
|
|
72
|
+
for issue in issues:
|
|
73
|
+
parts.append(f"--- #{issue['number']}: {issue['title']} ---")
|
|
74
|
+
parts.append(issue["body"] if issue["body"] else "(no body)")
|
|
75
|
+
parts.append("")
|
|
76
|
+
else:
|
|
77
|
+
parts.append(" (none fetched)")
|
|
78
|
+
|
|
79
|
+
parts.append("---")
|
|
80
|
+
parts.append(
|
|
81
|
+
f"This repo's acceptance driver is `{driver_kind}` — render the "
|
|
82
|
+
f"mock(s) in that medium as `{driver_mock_glob}` fixtures under "
|
|
83
|
+
f"`{ms_dir}/mocks/`, then write the black-box contract to "
|
|
84
|
+
f"`{ms_dir}/contract.md` (docs/ORACLE_LOOP.md \"Layout\"). If "
|
|
85
|
+
f"`{ms_dir}/contract.md` already exists, you are AMENDING Gate A, "
|
|
86
|
+
"not authoring it from scratch — read it first and edit in place. "
|
|
87
|
+
"Commit and push both to this branch when done."
|
|
88
|
+
)
|
|
89
|
+
return "\n".join(parts)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_mock_author_amend_briefing(
|
|
93
|
+
*,
|
|
94
|
+
repo_slug: str,
|
|
95
|
+
milestone_title: str,
|
|
96
|
+
milestone_number: int,
|
|
97
|
+
tracking_issue_number: int,
|
|
98
|
+
amend_text: str,
|
|
99
|
+
) -> str:
|
|
100
|
+
"""#1315: seed briefing for a *targeted* Gate-A contract amendment.
|
|
101
|
+
|
|
102
|
+
Unlike :func:`build_mock_author_briefing` (a full fresh render from the
|
|
103
|
+
milestone's open-issues digest), this skips re-fetching milestone
|
|
104
|
+
context entirely and hands the mock-author exactly the operator's own
|
|
105
|
+
description of what to correct in the already-merged
|
|
106
|
+
``tests/acceptance/ms-NN/contract.md`` (and/or its ``mocks/``). This is
|
|
107
|
+
the "properly-typed contract-amend dispatch" #1315 closes the gap for:
|
|
108
|
+
before this, the only way to land a small, targeted correction to a
|
|
109
|
+
merged Gate-A contract was a plain ``coord assign`` (defaulting to
|
|
110
|
+
``type="work"``) — the root cause of #1314's three-way breakage. Still
|
|
111
|
+
dispatches as ``type="mock-author"`` (independently-typed, exempt from
|
|
112
|
+
the ``tests/acceptance/**`` sealing guard — see ``coord/dispatch.py``
|
|
113
|
+
and #1315's ``_sealed_write_guard_tools`` in ``coord/agent.py``), so a
|
|
114
|
+
contract correction never needs to fall back to ``type="work"`` again.
|
|
115
|
+
"""
|
|
116
|
+
ms_dir = f"tests/acceptance/{ms_dirname(milestone_number)}"
|
|
117
|
+
|
|
118
|
+
parts: list[str] = []
|
|
119
|
+
parts.append(
|
|
120
|
+
f"=== Gate A CONTRACT AMENDMENT for {repo_slug} — milestone "
|
|
121
|
+
f"{milestone_title!r} (tracking #{tracking_issue_number}) ===\n"
|
|
122
|
+
)
|
|
123
|
+
parts.append(
|
|
124
|
+
f"This is a targeted correction to the ALREADY-MERGED Gate-A "
|
|
125
|
+
f"contract at `{ms_dir}/contract.md` (and/or its mocks under "
|
|
126
|
+
f"`{ms_dir}/mocks/`) — NOT a from-scratch render. Read the existing "
|
|
127
|
+
f"`{ms_dir}/contract.md` first, then apply exactly the correction "
|
|
128
|
+
"described below. Do not regenerate the document from scratch, and "
|
|
129
|
+
f"do not touch any file outside `{ms_dir}/`. Commit and push both "
|
|
130
|
+
"to this branch when done."
|
|
131
|
+
)
|
|
132
|
+
parts.append("")
|
|
133
|
+
parts.append("--- Requested correction ---")
|
|
134
|
+
parts.append(amend_text.strip())
|
|
135
|
+
return "\n".join(parts)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def dispatch_acceptance_mock(
|
|
139
|
+
repo_name: str,
|
|
140
|
+
tracking_issue_number: int,
|
|
141
|
+
config: Config,
|
|
142
|
+
*,
|
|
143
|
+
machine_override: str | None = None,
|
|
144
|
+
path: str | None = None,
|
|
145
|
+
amend_briefing: str | None = None,
|
|
146
|
+
) -> tuple[str, str]:
|
|
147
|
+
"""End-to-end: resolve the milestone, pick a machine, seed the
|
|
148
|
+
briefing, dispatch a ``type="mock-author"`` assignment.
|
|
149
|
+
|
|
150
|
+
*path* (#1125, repo-root-relative, e.g. ``"coord/foo.py"``) resolves
|
|
151
|
+
which driver to use when the repo's acceptance config is routed
|
|
152
|
+
(``acceptance.drivers.<repo>.routes``) — pass the milestone's
|
|
153
|
+
representative subtree (see `AcceptanceConfig.driver_for` for the
|
|
154
|
+
single-path-per-call resolution rule). Unused when the repo has a flat,
|
|
155
|
+
unrouted driver.
|
|
156
|
+
|
|
157
|
+
*amend_briefing* (#1315): when given, skip the full fresh-render
|
|
158
|
+
briefing (:func:`build_mock_author_briefing`, which re-fetches every
|
|
159
|
+
open issue under the milestone) in favor of a narrow, targeted
|
|
160
|
+
amendment briefing (:func:`build_mock_author_amend_briefing`) built
|
|
161
|
+
from this exact text — the "small, targeted fix to an already-merged
|
|
162
|
+
contract" case #1314 hit with no properly-typed tool for it. Also
|
|
163
|
+
leaves ``target_branch`` unset (unlike the fresh-render path's
|
|
164
|
+
``ms-{N}-gate-a``) so the worker gets a normal auto-named branch off the
|
|
165
|
+
milestone's current base — the original gate-a branch was already
|
|
166
|
+
merged (and likely deleted) by the time an amendment is needed.
|
|
167
|
+
|
|
168
|
+
Returns ``(assignment_id, machine_name)``. Raises ``RuntimeError`` when
|
|
169
|
+
the repo is unknown, has no acceptance driver configured (or, for a
|
|
170
|
+
routed repo, no driver resolves for *path*), the tracking issue can't be
|
|
171
|
+
fetched or has no milestone, the milestone's Gate A is already claimed,
|
|
172
|
+
no machine claims the repo, or the agent rejects the dispatch.
|
|
173
|
+
"""
|
|
174
|
+
repo_cfg = config.repo(repo_name)
|
|
175
|
+
if repo_cfg is None:
|
|
176
|
+
raise RuntimeError(f"repo {repo_name!r} not in coordinator.yml")
|
|
177
|
+
|
|
178
|
+
driver_cfg = config.acceptance.driver_for(repo_name, path)
|
|
179
|
+
if driver_cfg is None:
|
|
180
|
+
if config.acceptance.has_driver(repo_name):
|
|
181
|
+
raise RuntimeError(
|
|
182
|
+
f"repo {repo_name!r} has a routed acceptance driver "
|
|
183
|
+
"(acceptance.drivers routes) but no route matched — pass "
|
|
184
|
+
"--for-path to select the milestone's subtree (e.g. "
|
|
185
|
+
"'coord/**')"
|
|
186
|
+
)
|
|
187
|
+
raise RuntimeError(
|
|
188
|
+
f"repo {repo_name!r} has no acceptance driver configured — add "
|
|
189
|
+
"it under acceptance.drivers in coordinator.yml before running "
|
|
190
|
+
"Gate A (docs/ORACLE_LOOP.md)"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
issue_data = github_ops.get_issue(repo_cfg.github, tracking_issue_number)
|
|
195
|
+
except RuntimeError as e:
|
|
196
|
+
raise RuntimeError(f"could not fetch #{tracking_issue_number}: {e}") from e
|
|
197
|
+
|
|
198
|
+
milestone = issue_data.get("milestone") or {}
|
|
199
|
+
milestone_number = milestone.get("number")
|
|
200
|
+
if milestone_number is None:
|
|
201
|
+
raise RuntimeError(f"#{tracking_issue_number} has no milestone")
|
|
202
|
+
milestone_title = milestone.get("title") or f"#{milestone_number}"
|
|
203
|
+
|
|
204
|
+
from coord import board_service # noqa: PLC0415
|
|
205
|
+
|
|
206
|
+
board = board_service.read_board()
|
|
207
|
+
|
|
208
|
+
claim = find_work_claim(tracking_issue_number, repo_name, repo_cfg.github, board)
|
|
209
|
+
if claim is not None:
|
|
210
|
+
# #1059: make the refusal actionable. The operator's "PERMANENTLY
|
|
211
|
+
# STUCK" report (#1041) was a stale claim they "found no way to clear
|
|
212
|
+
# through normal coord commands" — so name the escape hatch. A live
|
|
213
|
+
# claim means a real Gate A worker is already running (leave it); a
|
|
214
|
+
# dead one is cleared by `coord diagnose <repo> <issue>`.
|
|
215
|
+
raise RuntimeError(
|
|
216
|
+
f"Gate A already in flight: {claim_message(claim)} — if that "
|
|
217
|
+
f"session is dead, clear it with `coord diagnose {repo_name} "
|
|
218
|
+
f"{tracking_issue_number}`, then dispatch again"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Pick the machine.
|
|
222
|
+
if machine_override:
|
|
223
|
+
machine = next(
|
|
224
|
+
(m for m in config.machines if m.name == machine_override),
|
|
225
|
+
None,
|
|
226
|
+
)
|
|
227
|
+
if machine is None:
|
|
228
|
+
raise RuntimeError(f"machine {machine_override!r} not in coordinator.yml")
|
|
229
|
+
if not machine.can_work_on(repo_name):
|
|
230
|
+
raise RuntimeError(
|
|
231
|
+
f"machine {machine_override!r} does not list repo {repo_name!r}"
|
|
232
|
+
)
|
|
233
|
+
else:
|
|
234
|
+
picked: Machine | None = pick_machine(repo_name, board, config)
|
|
235
|
+
if picked is None:
|
|
236
|
+
raise RuntimeError(
|
|
237
|
+
f"no idle machine claims repo {repo_name!r} — mock-author "
|
|
238
|
+
"needs a machine that has the repo cloned"
|
|
239
|
+
)
|
|
240
|
+
machine = picked
|
|
241
|
+
|
|
242
|
+
tracking_title = issue_data.get("title") or f"Milestone #{tracking_issue_number}"
|
|
243
|
+
resolved_model = config.models.default
|
|
244
|
+
|
|
245
|
+
if amend_briefing is not None:
|
|
246
|
+
# #1315: targeted amend — skip the full open-issues fetch, the
|
|
247
|
+
# fresh-render briefing, and reusing the (likely already-merged and
|
|
248
|
+
# deleted) original gate-a branch name.
|
|
249
|
+
briefing = build_mock_author_amend_briefing(
|
|
250
|
+
repo_slug=repo_cfg.github,
|
|
251
|
+
milestone_title=milestone_title,
|
|
252
|
+
milestone_number=milestone_number,
|
|
253
|
+
tracking_issue_number=tracking_issue_number,
|
|
254
|
+
amend_text=amend_briefing,
|
|
255
|
+
)
|
|
256
|
+
proposal = Proposal(
|
|
257
|
+
id=0,
|
|
258
|
+
machine_name=machine.name,
|
|
259
|
+
repo_name=repo_name,
|
|
260
|
+
issue_number=tracking_issue_number,
|
|
261
|
+
issue_title=f"[gate-a-amend] {tracking_title} — contract correction",
|
|
262
|
+
rationale="Gate A contract amendment (coord acceptance mock --amend, #1315)",
|
|
263
|
+
briefing=briefing,
|
|
264
|
+
model=resolved_model,
|
|
265
|
+
type="mock-author",
|
|
266
|
+
required_gates=list(config.pipeline.default_gates),
|
|
267
|
+
)
|
|
268
|
+
else:
|
|
269
|
+
issues = _fetch_milestone_issues(repo_cfg.github, milestone_number)
|
|
270
|
+
|
|
271
|
+
briefing = build_mock_author_briefing(
|
|
272
|
+
repo_slug=repo_cfg.github,
|
|
273
|
+
milestone_title=milestone_title,
|
|
274
|
+
milestone_number=milestone_number,
|
|
275
|
+
tracking_issue_number=tracking_issue_number,
|
|
276
|
+
tracking_issue_body=issue_data.get("body") or "",
|
|
277
|
+
issues=issues,
|
|
278
|
+
driver_kind=driver_cfg.kind,
|
|
279
|
+
driver_mock_glob=driver_cfg.mock,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
proposal = Proposal(
|
|
283
|
+
id=0,
|
|
284
|
+
machine_name=machine.name,
|
|
285
|
+
repo_name=repo_name,
|
|
286
|
+
issue_number=tracking_issue_number,
|
|
287
|
+
issue_title=f"[gate-a] {tracking_title} — mock + contract",
|
|
288
|
+
rationale="Gate A mock-author dispatch (coord acceptance mock, #930)",
|
|
289
|
+
briefing=briefing,
|
|
290
|
+
model=resolved_model,
|
|
291
|
+
type="mock-author",
|
|
292
|
+
required_gates=list(config.pipeline.default_gates),
|
|
293
|
+
target_branch=f"ms-{milestone_number}-gate-a",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
from coord.dispatch import dispatch_with_retry, post_briefing # noqa: PLC0415
|
|
297
|
+
from coord.state import record_dispatched # noqa: PLC0415
|
|
298
|
+
|
|
299
|
+
# #1059 review: dispatch_with_retry can raise ValueError (bad machine/repo
|
|
300
|
+
# config) or httpx.HTTPError (agent unreachable/rejected the payload) —
|
|
301
|
+
# neither was previously caught here, so it propagated past this
|
|
302
|
+
# function's documented "raises RuntimeError" contract as a raw
|
|
303
|
+
# traceback. `acceptance_mock_cmd` only catches RuntimeError, so an
|
|
304
|
+
# uncaught one would dump a multi-line Python traceback to stderr instead
|
|
305
|
+
# of a clean `error: ...` line — exactly the "inconsistent/unreadable
|
|
306
|
+
# error" the operator hit, just via a different trigger than the one
|
|
307
|
+
# reproduced. Translate to RuntimeError so every failure path here is a
|
|
308
|
+
# single clean line, no partial dispatch is recorded (record_dispatched
|
|
309
|
+
# below never runs when this raises), and no claim is left dangling.
|
|
310
|
+
try:
|
|
311
|
+
response = dispatch_with_retry(
|
|
312
|
+
proposal,
|
|
313
|
+
config,
|
|
314
|
+
max_retries=config.concurrency.max_retries,
|
|
315
|
+
backoff_base=config.concurrency.backoff_base,
|
|
316
|
+
)
|
|
317
|
+
except (ValueError, httpx.HTTPError) as e:
|
|
318
|
+
raise RuntimeError(f"could not dispatch mock-author to {machine.name!r}: {e}") from e
|
|
319
|
+
|
|
320
|
+
assignment_id = response.get("id") or uuid.uuid4().hex[:12]
|
|
321
|
+
|
|
322
|
+
record_dispatched(
|
|
323
|
+
assignment_id=assignment_id,
|
|
324
|
+
proposal=proposal,
|
|
325
|
+
repo_github=repo_cfg.github,
|
|
326
|
+
provider_name=response.get("_provider_name"),
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
try:
|
|
330
|
+
post_briefing(proposal, config, assignment_id=assignment_id)
|
|
331
|
+
except Exception: # noqa: BLE001 — best-effort, mirrors dispatch_entry
|
|
332
|
+
pass
|
|
333
|
+
|
|
334
|
+
return assignment_id, machine.name
|