pilot-workers 0.5.2__tar.gz → 0.5.3__tar.gz
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.
- {pilot_workers-0.5.2/src/pilot_workers.egg-info → pilot_workers-0.5.3}/PKG-INFO +1 -1
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/pyproject.toml +1 -1
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/install.py +43 -5
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/integrations/claude-host/skills/pilot-workers/SKILL.md +1 -1
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/integrations/codex-host/skills/pilot-workers/SKILL.md +1 -1
- {pilot_workers-0.5.2 → pilot_workers-0.5.3/src/pilot_workers.egg-info}/PKG-INFO +1 -1
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_integrations.py +30 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_skill_generation.py +89 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/LICENSE +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/README.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/setup.cfg +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/__init__.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/__main__.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/__init__.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/dispatch.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/fanout.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/main.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/run.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/cli/status.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/credentials.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/permissions/README.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/permissions/relaxed.yaml +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/permissions/strict.yaml +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/providers/README.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/providers/ds.yaml +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/providers/glm.yaml +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/providers/kimi-k3.yaml +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/templates/code.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/templates/explore.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/templates/review.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/data/templates/test.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/fmt_events.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/integrations/README.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/maintain.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/policy.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/prompts/code.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/prompts/common.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/prompts/explore.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/prompts/review.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/prompts/test.md +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/providers.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/runners/__init__.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/runners/base.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/runners/opencode_runner.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/runtime.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/scripts/install_runtime.sh +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers/taskguard.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers.egg-info/SOURCES.txt +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers.egg-info/dependency_links.txt +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers.egg-info/entry_points.txt +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers.egg-info/requires.txt +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/src/pilot_workers.egg-info/top_level.txt +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_child_env_hygiene.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_cli_main.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_credentials.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_dispatch.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_fanout.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_fanout_watchdog.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_fmt_events.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_global_key.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_install.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_isolation_guard.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_lock_takeover.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_maintain.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_manifest_locking.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_opencode_runner.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_policy.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_preflight.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_providers.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_render_unified.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_result_extract.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_run_cli.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_runner_cache.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_runner_seams.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_runtime.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_sandbox.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_self_description.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_status.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_status_config.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_task_guards.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_version_cache.py +0 -0
- {pilot_workers-0.5.2 → pilot_workers-0.5.3}/tests/test_worker_prompts.py +0 -0
|
@@ -495,15 +495,52 @@ def apply_generated_region(skill_text: str, region: str) -> str:
|
|
|
495
495
|
return before + GENERATED_BEGIN + inner + GENERATED_END + after
|
|
496
496
|
|
|
497
497
|
|
|
498
|
+
def render_trigger_sentence(provider_keys: list[str],
|
|
499
|
+
modes: dict[str, str]) -> str:
|
|
500
|
+
"""The frontmatter's trigger clause, generated from the host's ROUTING.
|
|
501
|
+
|
|
502
|
+
This is the only part of the skill a model sees before deciding whether to
|
|
503
|
+
load it, so it decides what the whole feature can do. It used to read
|
|
504
|
+
"Trigger when the user names a worker provider — ds, kimi-k3", which is the
|
|
505
|
+
most specific clause in the description and therefore the operative one — so
|
|
506
|
+
"explore this codebase" never loaded the skill, and the mode -> provider table
|
|
507
|
+
(which lives in the BODY) was never read. The assignment recorded by
|
|
508
|
+
``install ds on claude for explore`` was unreachable at trigger time: stored,
|
|
509
|
+
displayed by ``status``, rendered into the skill, and unable to fire.
|
|
510
|
+
|
|
511
|
+
The point of the assignment is that the user does NOT name a provider. So the
|
|
512
|
+
modes come first, in an explicit ``mode, mode -> provider`` form, and naming a
|
|
513
|
+
provider stays as an additional path. A host with no assignments routes
|
|
514
|
+
nothing and must not claim otherwise.
|
|
515
|
+
"""
|
|
516
|
+
names = ", ".join(provider_keys)
|
|
517
|
+
routed: dict[str, list[str]] = {}
|
|
518
|
+
for mode, key in sorted(modes.items()):
|
|
519
|
+
if key in provider_keys:
|
|
520
|
+
routed.setdefault(key, []).append(mode)
|
|
521
|
+
if not routed:
|
|
522
|
+
return (f"No mode is routed on this host yet, so nothing is delegated "
|
|
523
|
+
f"automatically; naming a provider ({names}) triggers this skill.")
|
|
524
|
+
groups = "; ".join(
|
|
525
|
+
f"{', '.join(sorted(mode_list))} \u2192 {key}"
|
|
526
|
+
for key, mode_list in sorted(routed.items()))
|
|
527
|
+
return (f"Route by MODE, not by name: {groups}. A request for that kind of "
|
|
528
|
+
f"work triggers this skill even when the user names no provider — "
|
|
529
|
+
f"which is the point of the routing — and naming one ({names}) "
|
|
530
|
+
f"triggers it too.")
|
|
531
|
+
|
|
532
|
+
|
|
498
533
|
def substitute_trigger_placeholder(skill_text: str,
|
|
499
|
-
provider_keys: list[str]
|
|
500
|
-
|
|
534
|
+
provider_keys: list[str],
|
|
535
|
+
modes: dict[str, str] | None = None) -> str:
|
|
536
|
+
"""Fill the frontmatter trigger token.
|
|
501
537
|
|
|
502
538
|
Pure string function: a host's deployed skill names exactly the providers
|
|
503
539
|
configured for it, so "use <provider>" reaches the skill without leaking
|
|
504
540
|
the names of providers the host never got.
|
|
505
541
|
"""
|
|
506
|
-
return skill_text.replace(
|
|
542
|
+
return skill_text.replace(
|
|
543
|
+
TRIGGER_PLACEHOLDER, render_trigger_sentence(provider_keys, modes or {}))
|
|
507
544
|
|
|
508
545
|
|
|
509
546
|
def _frontmatter_block(text: str) -> str | None:
|
|
@@ -537,7 +574,8 @@ def _render_deployed_skill(installs: dict, host: str, base_text: str) -> str:
|
|
|
537
574
|
could never drop a provider that was uninstalled later.
|
|
538
575
|
"""
|
|
539
576
|
provider_keys = host_providers(installs, host)
|
|
540
|
-
|
|
577
|
+
modes = host_modes(installs, host)
|
|
578
|
+
region = render_worker_region(provider_keys, modes)
|
|
541
579
|
try:
|
|
542
580
|
out = apply_generated_region(base_text, region)
|
|
543
581
|
except RuntimeError:
|
|
@@ -551,7 +589,7 @@ def _render_deployed_skill(installs: dict, host: str, base_text: str) -> str:
|
|
|
551
589
|
out = apply_generated_region(_packaged_skill_text(host), region)
|
|
552
590
|
new_front = _frontmatter_block(
|
|
553
591
|
substitute_trigger_placeholder(_packaged_skill_text(host),
|
|
554
|
-
provider_keys))
|
|
592
|
+
provider_keys, modes))
|
|
555
593
|
old_front = _frontmatter_block(out)
|
|
556
594
|
if new_front is not None and old_front is not None:
|
|
557
595
|
out = new_front + out[len(old_front):]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pilot-workers
|
|
3
|
-
description: Dispatch bounded tasks (code, explore, test, review, resume) to isolated LLM workers via the pilot-workers CLI and harvest their structured verdicts. Use whenever a task is worth delegating — bulk mechanical edits, codebase exploration, long test runs, multi-axis code review — so the main session keeps its context for planning and verification. Not for small tweaks, tasks needing mid-course judgment, or anything touching credentials/CI/production.
|
|
3
|
+
description: Dispatch bounded tasks (code, explore, test, review, resume) to isolated LLM workers via the pilot-workers CLI and harvest their structured verdicts. Use whenever a task is worth delegating — bulk mechanical edits, codebase exploration, long test runs, multi-axis code review — so the main session keeps its context for planning and verification. Not for small tweaks, tasks needing mid-course judgment, or anything touching credentials/CI/production. {{PILOT_PROVIDER_TRIGGERS}}
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# pilot-workers Playbook
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pilot-workers
|
|
3
|
-
description: Plan with Codex, then run bounded tasks (code, explore, test, review, resume) through isolated LLM workers via the pilot-workers CLI and verify their structured verdicts. Invoke explicitly as `$pilot-workers [code|explore|test|review|resume] [task]`. Use whenever
|
|
3
|
+
description: Plan with Codex, then run bounded tasks (code, explore, test, review, resume) through isolated LLM workers via the pilot-workers CLI and verify their structured verdicts. Invoke explicitly as `$pilot-workers [code|explore|test|review|resume] [task]`. Use whenever coding, investigation, testing, review or session continuation is worth handing to a worker model. {{PILOT_PROVIDER_TRIGGERS}} Not for small tweaks, mid-course judgment calls, or credentials/CI/production changes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# pilot-workers Playbook
|
|
@@ -150,3 +150,33 @@ def test_common_prompt_exists_and_has_no_result_block():
|
|
|
150
150
|
assert RESULT_END not in text, (
|
|
151
151
|
"common.md must not contain the result block (it is per-mode)"
|
|
152
152
|
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_no_host_template_hardcodes_its_own_trigger_condition():
|
|
156
|
+
"""Both templates must hand the whole trigger clause to the placeholder.
|
|
157
|
+
|
|
158
|
+
The clause is generated from the host's routing, so any wording left in the
|
|
159
|
+
template competes with it — and the first pass at this fix patched the claude
|
|
160
|
+
template and missed the codex one, which phrased the same sentence
|
|
161
|
+
differently. Sibling-miss number twelve; the guard costs four lines.
|
|
162
|
+
"""
|
|
163
|
+
from pathlib import Path as _Path
|
|
164
|
+
|
|
165
|
+
import pilot_workers
|
|
166
|
+
from pilot_workers.cli import install as install_mod
|
|
167
|
+
|
|
168
|
+
root = _Path(pilot_workers.__file__).resolve().parent / "integrations"
|
|
169
|
+
offenders = []
|
|
170
|
+
for host in ("claude", "codex"):
|
|
171
|
+
path = root / f"{host}-host" / "skills" / "pilot-workers" / "SKILL.md"
|
|
172
|
+
front = install_mod._frontmatter_block(
|
|
173
|
+
path.read_text(encoding="utf-8")) or ""
|
|
174
|
+
normalised = " ".join(front.split())
|
|
175
|
+
assert install_mod.TRIGGER_PLACEHOLDER in normalised, (
|
|
176
|
+
f"{host}: the template lost its trigger placeholder")
|
|
177
|
+
for phrase in ("Trigger when the user names",
|
|
178
|
+
"asks to delegate",
|
|
179
|
+
"to a worker model —"):
|
|
180
|
+
if phrase in normalised:
|
|
181
|
+
offenders.append(f"{host}: still hardcodes {phrase!r}")
|
|
182
|
+
assert not offenders, "; ".join(offenders)
|
|
@@ -1177,3 +1177,92 @@ def test_a_file_dropped_from_the_packaged_tree_stops_being_deployed(isolated, ca
|
|
|
1177
1177
|
recorded = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
1178
1178
|
assert str(stale) not in recorded["installs"]["claude"]["files"]
|
|
1179
1179
|
assert "removed stale" in capsys.readouterr().out
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
# ----------------------------------------------------------------------
|
|
1183
|
+
# The trigger must fire on the WORK, not on a provider name.
|
|
1184
|
+
#
|
|
1185
|
+
# The whole point of `install ds on claude for explore` is that the user says
|
|
1186
|
+
# "explore this codebase" and the system picks ds. The generated description
|
|
1187
|
+
# ended with "Trigger when the user names a worker provider — ds, kimi-k3",
|
|
1188
|
+
# which is the most specific clause in it and therefore the operative one, so a
|
|
1189
|
+
# prompt that named no provider never loaded the skill — and the mode -> provider
|
|
1190
|
+
# table lives in the BODY, readable only after loading. The assignment was
|
|
1191
|
+
# unreachable at trigger time: recorded, displayed by `status`, rendered into the
|
|
1192
|
+
# skill, and never able to fire.
|
|
1193
|
+
# ----------------------------------------------------------------------
|
|
1194
|
+
|
|
1195
|
+
def _deployed_description(isolated, providers_and_modes):
|
|
1196
|
+
"""Install a host with the given config and return its rendered description."""
|
|
1197
|
+
with install_mod.manifest_transaction() as installs:
|
|
1198
|
+
for key in providers_and_modes["providers"]:
|
|
1199
|
+
install_mod.add_host_provider(installs, "claude", key)
|
|
1200
|
+
for mode, key in providers_and_modes["modes"].items():
|
|
1201
|
+
install_mod.set_host_mode(installs, "claude", mode, key)
|
|
1202
|
+
install_mod.main(["claude", "--target", str(isolated["target"])])
|
|
1203
|
+
text = _claude_skill(isolated).read_text(encoding="utf-8")
|
|
1204
|
+
front = install_mod._frontmatter_block(text)
|
|
1205
|
+
assert front is not None
|
|
1206
|
+
return " ".join(front.split())
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
def test_the_description_triggers_on_an_assigned_mode(isolated):
|
|
1210
|
+
"""A prompt like "explore this codebase" must be able to match.
|
|
1211
|
+
|
|
1212
|
+
Asserted on the exact ROUTING form, not on the mode word: the description's
|
|
1213
|
+
first sentence already lists every mode ("bounded tasks (code, explore,
|
|
1214
|
+
test, review, resume)"), so a substring check for "explore" passes whatever
|
|
1215
|
+
the trigger says. The first version of this test did exactly that and passed
|
|
1216
|
+
against the broken description.
|
|
1217
|
+
"""
|
|
1218
|
+
description = _deployed_description(isolated, {
|
|
1219
|
+
"providers": ["ds", "kimi-k3"],
|
|
1220
|
+
"modes": {"explore": "ds", "test": "ds",
|
|
1221
|
+
"code": "kimi-k3", "review": "kimi-k3"},
|
|
1222
|
+
})
|
|
1223
|
+
assert "explore, test → ds" in description, description
|
|
1224
|
+
assert "code, review → kimi-k3" in description, description
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
def test_the_description_no_longer_makes_a_provider_name_the_condition(isolated):
|
|
1228
|
+
"""The narrowing sentence that defeated the whole feature must be gone."""
|
|
1229
|
+
description = _deployed_description(isolated, {
|
|
1230
|
+
"providers": ["ds"], "modes": {"explore": "ds"},
|
|
1231
|
+
})
|
|
1232
|
+
assert "Trigger when the user names a worker provider" not in description, (
|
|
1233
|
+
"the operative clause still requires the user to name a provider")
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
def test_naming_a_provider_directly_still_triggers(isolated):
|
|
1237
|
+
"""The old path must survive: "use ds to ..." is still a valid way in."""
|
|
1238
|
+
description = _deployed_description(isolated, {
|
|
1239
|
+
"providers": ["ds"], "modes": {"explore": "ds"},
|
|
1240
|
+
})
|
|
1241
|
+
assert "ds" in description
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
def test_a_host_with_no_mode_assignments_does_not_claim_mode_triggering(isolated):
|
|
1245
|
+
"""Nothing is routed, so the skill must not advertise that it handles a mode."""
|
|
1246
|
+
description = _deployed_description(isolated, {
|
|
1247
|
+
"providers": ["ds"], "modes": {},
|
|
1248
|
+
})
|
|
1249
|
+
assert "ds" in description
|
|
1250
|
+
for mode in ("explore", "test", "review"):
|
|
1251
|
+
assert f"{mode} →" not in description and f"{mode} ->" not in description, (
|
|
1252
|
+
f"a host with no assignments claims to route {mode}")
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
def test_an_unassigned_mode_is_not_advertised(isolated):
|
|
1256
|
+
"""Only what is actually routed. `code` has no provider here."""
|
|
1257
|
+
description = _deployed_description(isolated, {
|
|
1258
|
+
"providers": ["ds"], "modes": {"explore": "ds"},
|
|
1259
|
+
})
|
|
1260
|
+
routing = install_mod.render_trigger_sentence(["ds"], {"explore": "ds"})
|
|
1261
|
+
assert "explore → ds" in routing
|
|
1262
|
+
# Scoped to the GENERATED clause: the hand-written prose above it legitimately
|
|
1263
|
+
# mentions "bulk mechanical edits" as an example of delegable work, so a check
|
|
1264
|
+
# against the whole description flagged text that was never the problem.
|
|
1265
|
+
for mode in ("code", "review", "test"):
|
|
1266
|
+
assert f"{mode} →" not in routing, (
|
|
1267
|
+
f"the routing clause advertises {mode}, which nothing is assigned to")
|
|
1268
|
+
assert "explore → ds" in description, description
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|