loopagent-gate 0.4.2__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.
- loopagent_gate-0.4.2.dist-info/METADATA +374 -0
- loopagent_gate-0.4.2.dist-info/RECORD +40 -0
- loopagent_gate-0.4.2.dist-info/WHEEL +5 -0
- loopagent_gate-0.4.2.dist-info/entry_points.txt +2 -0
- loopagent_gate-0.4.2.dist-info/licenses/LICENSE +21 -0
- loopagent_gate-0.4.2.dist-info/top_level.txt +1 -0
- loopgate/__init__.py +7 -0
- loopgate/__main__.py +8 -0
- loopgate/cli.py +147 -0
- loopgate/gates/anchor_witness.py +249 -0
- loopgate/gates/assert_dims.py +88 -0
- loopgate/gates/bootstrap_state.py +111 -0
- loopgate/gates/change_health.py +232 -0
- loopgate/gates/check_loop.py +1267 -0
- loopgate/gates/check_product.py +463 -0
- loopgate/gates/check_visual.py +224 -0
- loopgate/gates/doctor.py +130 -0
- loopgate/gates/estimate_task.py +137 -0
- loopgate/gates/export_public.py +289 -0
- loopgate/gates/guard_card.py +519 -0
- loopgate/gates/loop_watch.py +118 -0
- loopgate/gates/regressions.py +463 -0
- loopgate/gates/render_status_page.py +151 -0
- loopgate/gates/render_task_ledger.py +312 -0
- loopgate/gates/review_pr.py +370 -0
- loopgate/gates/sync_issues_to_kanban.py +593 -0
- loopgate/gates/tree_parity.py +307 -0
- loopgate/gates/worker_brief.py +396 -0
- loopgate/init.py +128 -0
- loopgate/paths.py +43 -0
- loopgate/py.typed +0 -0
- loopgate/templates/__init__.py +1 -0
- loopgate/templates/configs/anchor_witness.example.json +14 -0
- loopgate/templates/configs/fixtures/issues_sample.json +75 -0
- loopgate/templates/configs/fixtures/review_approve.json +5 -0
- loopgate/templates/configs/fixtures/review_changes.json +5 -0
- loopgate/templates/configs/fixtures/review_no_verdict.json +5 -0
- loopgate/templates/configs/repo_profiles.example.json +63 -0
- loopgate/templates/configs/tree_parity.example.json +16 -0
- loopgate/templates/skills/loop-engineering/SKILL.md +281 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"runId": "run_fixture_approve_001",
|
|
3
|
+
"status": "ok",
|
|
4
|
+
"summary": "## Findings\nNo blockers found. The design is clean and matches the acceptance criteria.\n\n## Missing tests or evidence\nNone; the mandatory evidence bundle was proved and reproduced.\n\n## Questions for the author\nNone.\n\nVERDICT: APPROVE"
|
|
5
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{
|
|
2
|
+
"runId": "run_fixture_changes_001",
|
|
3
|
+
"status": "ok",
|
|
4
|
+
"summary": "## Findings\n- `scripts/review_pr.py`: line 42: major: failure to handle missing verdict line leads to undefined behaviour.\n\n## Missing tests or evidence\nMissing offline unit test coverage for error conditions.\n\n## Questions for the author\nCan we avoid network calls when reading from result files?\n\nVERDICT: CHANGES"
|
|
5
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"note": "PER-REPOSITORY CONTRACT — this is the example shipped with the public tree, copied to configs/repo_profiles.json by loopgate/gates/export_public.py. Replace it with your own repository: the evidence bundle is the machine-readable form of the rule that a worker must prove EVERY gate its repository requires, not a subset. Add a profile before queueing issues for a new repository; the bridge refuses to mint a brief for an unknown profile (fail-closed). local_path may stay null: pass --repo-path, or set the environment variable named in local_path_env.",
|
|
4
|
+
"profiles": {
|
|
5
|
+
"sample-service": {
|
|
6
|
+
"repo": "example/sample-service",
|
|
7
|
+
"base_branch": "main",
|
|
8
|
+
"local_path": null,
|
|
9
|
+
"local_path_env": "EXAMPLE_REPO",
|
|
10
|
+
"sources_of_truth": [
|
|
11
|
+
"AGENTS.md",
|
|
12
|
+
"Makefile"
|
|
13
|
+
],
|
|
14
|
+
"evidence_bundle": [
|
|
15
|
+
{
|
|
16
|
+
"command": "make build",
|
|
17
|
+
"source": "Makefile target 'build': a change is not accepted uncompiled"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"command": "make lint",
|
|
21
|
+
"source": "AGENTS.md -> code quality rules, item 1 (mandatory before any commit, 0 issues)"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"command": "make test",
|
|
25
|
+
"source": "AGENTS.md -> code quality rules, item 4 (all tests pass before creating a commit)"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"bundle_source": "AGENTS.md -> 'code quality rules (mandatory before any commit)' plus the Makefile build target",
|
|
29
|
+
"branch_pattern": "<role>/issue-<n>",
|
|
30
|
+
"pr_rules": [
|
|
31
|
+
"one task = one PR: exactly one issue, one branch, one pull request",
|
|
32
|
+
"PR body embeds the evidence bundle verbatim (command, output, exit code)",
|
|
33
|
+
"branch <role>/issue-<n> off main; never commit or push to main",
|
|
34
|
+
"changes stay inside this repository; no infra/GitOps repos",
|
|
35
|
+
"the reviewer re-runs the bundle in a separate worktree of the PR head commit: an unproven command is a REJECT regardless of the code"
|
|
36
|
+
],
|
|
37
|
+
"kanban": {
|
|
38
|
+
"assignee": "dev",
|
|
39
|
+
"assignee_by_label": {
|
|
40
|
+
"qa": "qa",
|
|
41
|
+
"ux": "qa"
|
|
42
|
+
},
|
|
43
|
+
"project": "example",
|
|
44
|
+
"workspace": null,
|
|
45
|
+
"priority": 0,
|
|
46
|
+
"note": "project=<slug> anchors the task in an isolated git worktree of the target clone with a deterministic branch; the worker commits and pushes that branch and opens the PR itself when `gh auth status` succeeds. Without an execution backend use --target backlog."
|
|
47
|
+
},
|
|
48
|
+
"triage_labels": [
|
|
49
|
+
"valid",
|
|
50
|
+
"triaged",
|
|
51
|
+
"approved"
|
|
52
|
+
],
|
|
53
|
+
"skip_labels": [
|
|
54
|
+
"invalid",
|
|
55
|
+
"wontfix",
|
|
56
|
+
"duplicate",
|
|
57
|
+
"needs-info",
|
|
58
|
+
"question",
|
|
59
|
+
"blocked"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"note": "Example parity policy for a project that is developed in one checkout and published from another. Copy it to configs/tree_parity.json (gitignored by convention in the tree that holds the real paths), fill in the two roots, then run `make parity POLICY=configs/tree_parity.json`. Every may-differ entry needs a reason: an exception without one is reported as drift regardless of the paths involved. Anything that differs and is covered by no rule at all is reported as unclassified drift, so a new file cannot be added to one checkout and forgotten in the other.",
|
|
3
|
+
"a": "/path/to/development/tree",
|
|
4
|
+
"b": "/path/to/published/tree",
|
|
5
|
+
"must_match": [
|
|
6
|
+
"loopgate/gates/*.py",
|
|
7
|
+
"Makefile",
|
|
8
|
+
".github/workflows/*.yml",
|
|
9
|
+
"pyproject.toml"
|
|
10
|
+
],
|
|
11
|
+
"may_differ": {
|
|
12
|
+
"loopgate/gates/private_tool.py": "development-only tooling that is not published",
|
|
13
|
+
"configs/*": "the development tree names real targets; the published tree carries sanitized ones",
|
|
14
|
+
"README.md": "the published README is rewritten for strangers"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: loop-engineering
|
|
3
|
+
description: Use when a task needs a bounded multi-round loop with evidence instead of a one-shot prompt — plan, verify with the local gate, repair, review, hand off. Trigger keywords: loop engineering, bounded loop, iterate until green, build fail repair prove, round-by-round evidence.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Loop Engineering (bounded loop with proof)
|
|
7
|
+
|
|
8
|
+
Превращает работу агента в ограниченную петлю, которую принимают по доказательству, а не по
|
|
9
|
+
уверенности: `Build → Fail → Repair → Prove → Review → Handoff`.
|
|
10
|
+
|
|
11
|
+
Скилл — это процедура. Гейт, который её принимает, — отдельный pip-пакет `loopgate`; раздел ниже
|
|
12
|
+
ставит его в проект. Ссылки на `course/**` в этом тексте — материал автора, в поставку не входит.
|
|
13
|
+
|
|
14
|
+
## Установка в проект (скилл скачан отдельно)
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install loopagent-gate # PyPI; or: pip install "git+https://github.com/mjeksonpx/loopagent.git@v0.4.2"
|
|
18
|
+
cd <проект> # git-репозиторий или каталог с AGENTS.md
|
|
19
|
+
loopgate init # этот скилл -> .agents/skills/, контракт -> AGENTS.md/PLANS.md/README.md, холодный старт
|
|
20
|
+
loopgate gate --strict # STOP: перечисляет измерения, которые проект настраивает сам
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Дистрибутив называется `loopagent-gate`, команда — `loopgate`: имя `loopgate` на PyPI занято чужим проектом,
|
|
24
|
+
а `loopagent` отклонён как слишком похожий на `loop-agent`.
|
|
25
|
+
Без консольной команды те же шаги: `python3 -m loopgate.init` и
|
|
26
|
+
`python3 -m loopgate.gates.check_loop --strict`.
|
|
27
|
+
|
|
28
|
+
Первый вердикт нового проекта — `STOP`, а не `PASS`: D10–D14 описывают факты самого проекта (бандл
|
|
29
|
+
команд, сценарии продукта, ключ подписи, визуальные сценарии, baseline метрик). `init` их не выдумывает
|
|
30
|
+
и не подставляет шаблон вместо доказательства.
|
|
31
|
+
|
|
32
|
+
В Hermes проектные скиллы грузятся только из доверенного репозитория: один раз `hermes skills trust`
|
|
33
|
+
из каталога проекта. В других агентах (Claude Code, Cursor, Copilot) `init` кладёт файл в ту же
|
|
34
|
+
кросс-инструментальную конвенцию `.agents/skills/`.
|
|
35
|
+
|
|
36
|
+
## Когда включать
|
|
37
|
+
|
|
38
|
+
- Работа требует нескольких раундов с накоплением evidence (readiness, безопасность, рефакторинг, релиз-гейт).
|
|
39
|
+
- Нужны состояние, артефакты раундов, stop-условия и финальный handoff.
|
|
40
|
+
- Нужен локальный гейт, который печатает PASS-маркер и `exit 0`.
|
|
41
|
+
|
|
42
|
+
## Когда НЕ включать
|
|
43
|
+
|
|
44
|
+
- Одноразовая правка текста/файла без раундов и evidence.
|
|
45
|
+
- Задачи, где достаточно `verify-changes` за один проход.
|
|
46
|
+
- Настройка расписания/крона как таковая (это автоматизация, а не содержание петли).
|
|
47
|
+
|
|
48
|
+
## Контракт рана (заполнить до первой правки)
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Goal: <что должно стать правдой>
|
|
52
|
+
Sources: <файлы/команды — источник истины, не выдумывать evidence>
|
|
53
|
+
Constraints: <что можно менять; что нельзя: сеть, секреты, вне репо>
|
|
54
|
+
Done-when: loopgate/gates/check_loop.py (python3 -m loopgate.gates.check_loop) → LOOP_ENGINEERING_PASS_0001, exit 0
|
|
55
|
+
Stop-if: нужного файла нет / нужна сеть или секреты / правка выходит за репо / гейт не зеленеет после 3 раундов
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Гейт (Prove)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python3 -m loopgate.gates.check_loop # обычный режим; опциональные измерения могут SKIP
|
|
62
|
+
python3 -m loopgate.gates.check_loop --strict # SKIP становится FAIL (после Этапа 2)
|
|
63
|
+
python3 -m loopgate.gates.check_loop --report artifacts/loop_gate_report.json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Гейт **read-only**: сам ничего не чинит и не пишет (кроме явного `--report`). Стартует красным —
|
|
67
|
+
печатает список failures и `LOOP_ENGINEERING_STOP_0001` с `exit 1`; после ремонта — `[OK]` по
|
|
68
|
+
измерениям и `LOOP_ENGINEERING_PASS_0001` с `exit 0`. Маркер печатает **только гейт**, не агент.
|
|
69
|
+
|
|
70
|
+
Guardrail-скан не должен ловить evidence: транскрипты раундов и собственные транскрипты гейта
|
|
71
|
+
цитируют текст failures. Поэтому в markdown вызовом считается только строка, начинающаяся с бинаря
|
|
72
|
+
агента, а исходник гейта и оба его транскрипта (`artifacts/loop_gate_report.json`,
|
|
73
|
+
`artifacts/loop_gate_pass.json`) исключены из скана целиком.
|
|
74
|
+
|
|
75
|
+
Приёмка проекта — только `--strict`: обычный режим печатает `acceptance: stage-1`, строгий —
|
|
76
|
+
`strict-complete`. Якорь `loop_gate_pass.json` пишется, когда зелены **все измерения кроме тех, что
|
|
77
|
+
читают сам якорь** — `D05` (связывает handoff с якорем) и `D12` (проверяет подпись якоря); хэш вердиктов
|
|
78
|
+
считается без обоих, иначе измерение сертифицировало бы само себя. Из этой пары следует восстановление
|
|
79
|
+
потерянного состояния: дерево без якоря не может его выпустить, если `D12` требуется для выпуска —
|
|
80
|
+
`D12` ждёт подписи, подпись ждёт якоря, якорь ждёт `D12`. Поэтому первый прогон после смены вердиктов
|
|
81
|
+
падает на `D05` (якорь устарел) и на `D12` (подписи нет), хэш из якоря прописывается в handoff, якорь
|
|
82
|
+
подписывается, и только следующий прогон даёт fixpoint.
|
|
83
|
+
|
|
84
|
+
| id | измерение | что проверяет |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| D01 | contract_skill | этот файл: контракт, маркер, команда гейта, правило ywcrew |
|
|
87
|
+
| D02 | sources_of_truth | `AGENTS.md` (Sources of truth / Done-when / Stop), `PLANS.md`, `README.md` |
|
|
88
|
+
| D03 | loop_state | `artifacts/loop_state.json`: схема, диапазоны 0..100, phase, маркер |
|
|
89
|
+
| D04 | rounds | ≥3 `artifacts/rounds/round_NN.md` с разделами Gap/Change/Evidence/Metrics/Next |
|
|
90
|
+
| D05 | handoff | `handoff/latest_loop.json` (status pass, нулевые blockers, непустой `reviewed_by`, существующие артефакты) + `handoff/supervisor_synthesis.md`; привязан к якорю `artifacts/loop_gate_pass.json`: хэш вердиктов без D05 **и** сверка состояния `(ok, skip)` якоря с состоянием текущего прогона — подделанный якорь не описывает текущий прогон и падает |
|
|
91
|
+
| D06 | guardrail_no_direct_cli | нет прямых вызовов CLI-агентов: код сканируется целиком, в markdown вызовом считается только строка, начинающаяся с бинаря агента (плюс строгий скан shell-блоков) |
|
|
92
|
+
| D07 | guardrail_no_secrets | нет строк, похожих на ключи/токены |
|
|
93
|
+
| D08 | course_corpus | `course/` собран: 17 уроков, ≥40k слов OCR, ≥15k байт промптов |
|
|
94
|
+
| D09 | roles_fanout | 4 артефакта ролей ≥300 символов с атрибуцией `runId:` (SKIP, если артефактов нет) |
|
|
95
|
+
| D10 | issue_bridge | этап 6: мост `sync_issues_to_kanban.py` + профили репозиториев; бриф из `worker_brief.py` содержит все команды бандла и правила доказательств; правило есть и в kanban-теле, и в `task.constraints` ywcrew-задачи; валидатор бандла атакован 5 фальсификациями (полный транскрипт принят; неполный, падающий, пустой и «заимствованный exit-код» — отклонены) |
|
|
96
|
+
|
|
97
|
+
| D11 | product_gate | этап 7: второй вердикт — пользовательские сценарии продукта (`configs/product_scenarios.json` + `check_product.py`); у каждого сценария строка `why`, у профиля — `local_path`; evaluator атакуется мутацией ожиданий каждого сценария (15 фальсификаций отклонено) |
|
|
98
|
+
|
|
99
|
+
| D12 | anchor_witness | якорь не сертифицирует сам себя (исключён из своего хэша вместе с D05); вердикт якоря подписан отдельным SSH-ключом (публичная половина в `configs/allowed_signers`), запись свидетеля совпадает с текущим вердиктом, перезапись якоря не верифицируется; внешний журнал объявлен (проверка сети — шаг оператора `check-external`) |
|
|
100
|
+
| D13 | visual_gate | визуальная половина qa: сценарии `configs/visual_scenarios.json` (у каждого `why`), headless-браузер реально доступен, минимум один сценарий требует скриншот, evaluator отклоняет 23 фальсифицированные страницы |
|
|
101
|
+
|
|
102
|
+
## Цикл
|
|
103
|
+
|
|
104
|
+
1. **Инспекция + план.** Прогнать гейт, взять список failures как план работ. Правок нет, ждать одобрения.
|
|
105
|
+
2. **Один раунд за раз.** Закрыть один gap → обновить `artifacts/loop_state.json` → добавить evidence → записать `artifacts/rounds/round_NN.md`.
|
|
106
|
+
3. **Метрики монотонны** в правильную сторону: readiness/evidence растут, risk/blockers падают.
|
|
107
|
+
4. **Перепрогон гейта** после значимых изменений; вывод гейта — единственный источник истины.
|
|
108
|
+
5. **Ревью отдельным движком:** `ywcrew panel` (codex + grok + agy/claude-sonnet). Оценщик имеет право сказать «нет».
|
|
109
|
+
6. **Стоп** только когда гейт печатает PASS-маркер. Затем — короткий handoff.
|
|
110
|
+
|
|
111
|
+
## Контекст и итерации (свежий контекст против `/loop`)
|
|
112
|
+
|
|
113
|
+
Правило взято из Ralph Loop (раздел 5 курса) и проверено на собственной практике: итерация должна
|
|
114
|
+
начинаться с **чистого контекста**, а память между итерациями живёт в файлах, не в окне модели.
|
|
115
|
+
|
|
116
|
+
- **Поток задач** (N карточек) идёт только через очередь: каждая карточка — новый процесс, свежий
|
|
117
|
+
контекст, бриф вместо накопленной истории. Воркер не помнит прошлую карточку и не должен.
|
|
118
|
+
- **`/loop` в сессии** крутит тот же контекст: модель тащит свои прошлые правки, окно растёт, после
|
|
119
|
+
десятков итераций качество падает. Годится только чтобы «дожать гейт до зелёного» на одной задаче.
|
|
120
|
+
Для потока из N задач `/loop` не использовать.
|
|
121
|
+
- **Читать в начале итерации:** `AGENTS.md`, свой бриф, `artifacts/loop_state.json`, последний
|
|
122
|
+
`artifacts/rounds/round_NN.md`, `git log --oneline -5`. Всё, что не в этих файлах — не существует.
|
|
123
|
+
- **Писать в конце итерации:** раунд-артефакт (или комментарий в карточку) с командой, выводом и
|
|
124
|
+
exit-кодом. Знание, оставшееся только в контексте, теряется вместе с ним.
|
|
125
|
+
- **Ремонт не бесконечен:** одна и та же команда, красная третий раз подряд, — это стоп и сдача с
|
|
126
|
+
выводом, а не десятая попытка (лимит закрепляется в брифе и в `verify`, см. PLANS.md п.33).
|
|
127
|
+
|
|
128
|
+
## Роли при fan-out (bounded, ровно четыре)
|
|
129
|
+
|
|
130
|
+
| Роль | Границы | Артефакт |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| explorer | read-only, карта репо и состояние гейта | `handoff/agents/explorer_findings.md` |
|
|
133
|
+
| reviewer | read-only, оспаривает слабое evidence | `handoff/agents/reviewer_risks.md` |
|
|
134
|
+
| worker | scoped edit, минимальная правка по evidence | `handoff/agents/worker_changes.md` |
|
|
135
|
+
| evaluator | верификация, прогон гейта | `handoff/agents/evaluator_verification.md` |
|
|
136
|
+
| supervisor (Hermes) | собирает evidence в одно решение | `handoff/supervisor_synthesis.md` |
|
|
137
|
+
|
|
138
|
+
`ywcrew panel` для reviewer, `ywcrew run --mode edit` для worker, read-only для explorer/evaluator.
|
|
139
|
+
Никаких прямых вызовов CLI-агентов — только через ywcrew.
|
|
140
|
+
|
|
141
|
+
Основной worker по умолчанию — `harness` (DeepSeek Harness, модель `deepseek-v4.1-flash` через
|
|
142
|
+
провайдера `opencode-go`): `ywcrew run --backend harness --mode edit`. DSH выполняет команды без
|
|
143
|
+
авто-отказов, но **песочницы в `dsh headless` нет** — read-only и edit одинаково идут в git-worktree,
|
|
144
|
+
поэтому запись в дерево оператора не попадает, а gitignored-состояние приезжает через
|
|
145
|
+
`.worktreeinclude`.
|
|
146
|
+
|
|
147
|
+
## Сколько ревьюеров нужно (цена против независимости)
|
|
148
|
+
|
|
149
|
+
- **По умолчанию — ОДИН ревьюер**, не панель: `ywcrew run --mode read-only --backend agy
|
|
150
|
+
--model claude-sonnet-4-6` (он в живом прогоне сам перепрогнал build+lint+test и снял возражение
|
|
151
|
+
фактом). Панель из трёх моделей стоит ~3× токенов и времени ревью.
|
|
152
|
+
- **Панель — только для высокой ставки**: релиз, безопасность, архитектурные решения, крупный PR,
|
|
153
|
+
или когда предыдущий вердикт оспорен. Три семейства моделей нужны там, где цена ошибки выше цены
|
|
154
|
+
ревью.
|
|
155
|
+
- Почему не «одной модели всегда достаточно»: в первом же живом PR-прогоне три члена панели дали
|
|
156
|
+
ACCEPT / REJECT / «нет вердикта». Одна модель = монетка по вопросу «принимать или нет». При этом
|
|
157
|
+
REJECT был по существу (неполные доказательства), и его снял именно перепрогон гейтов, а не голос.
|
|
158
|
+
|
|
159
|
+
## Доказательства воркера и консенсус панели (уроки живого PR-прогона)
|
|
160
|
+
|
|
161
|
+
- **Бандл доказательств = все гейты, которые требует `AGENTS.md` репозитория, а не подмножество.**
|
|
162
|
+
Воркер, доказавший только `go build`, получил законный REJECT: репозиторий требовал ещё
|
|
163
|
+
`golangci-lint run ./...` и `go test -count=1 ./...` перед коммитом. Правило переехало **в сам бриф**:
|
|
164
|
+
бриф рендерит `loopgate/gates/worker_brief.py` из `configs/repo_profiles.json` (команды + файл-источник
|
|
165
|
+
требования + формат транскрипта + output-контракт), а `worker_brief.py verify --profile <p>
|
|
166
|
+
--evidence <файл>` печатает `WORKER_EVIDENCE_PASS_0001` / `WORKER_EVIDENCE_STOP_0001` и называет
|
|
167
|
+
незапущенные команды, невидимые exit-коды и ненулевые возвраты. Прогонять verify до ревью —
|
|
168
|
+
дешевле, чем получать REJECT и доигрывать доказательства после.
|
|
169
|
+
- **Бриф — это тело задачи в kanban, а не отдельный документ.** `loopgate/gates/sync_issues_to_kanban.py`
|
|
170
|
+
берёт open issues (`gh`), отсеивает невалидные машино-проверяемым подмножеством (labels, объём тела,
|
|
171
|
+
наличие acceptance-критериев; семантический триаж остаётся роли), и создаёт задачу с брифом как
|
|
172
|
+
телом, идемпотентно по ключу `gh-<owner>-<repo>-<n>`. `--apply` отказывает, если у профиля не задан
|
|
173
|
+
workspace/project: очередь, которую некому исполнить, — не очередь.
|
|
174
|
+
- **Ревьюер, который перепрогоняет гейты сам, сильнее ревьюера, который судит по присланному.**
|
|
175
|
+
В том же прогоне второй член панели сам прогнал build+lint+test в отдельном worktree ветки и снял
|
|
176
|
+
возражение фактом. Разрыв «доказательства неполные» закрывается не спором, а прогоном гейтов на
|
|
177
|
+
head ветки (отдельный `git worktree` того же коммита — доказательство привязано к ревизии).
|
|
178
|
+
- **Exit-код принадлежит своей команде.** Требовать `exit=0` отдельной строкой *внутри блока этой
|
|
179
|
+
команды* (до следующей команды). Свободное окно «N строк после команды» пропускает один хвостовой
|
|
180
|
+
`exit=0`, который доказывает все гейты сразу — это нашёл живой ревьюер в первом же прогоне этапа 6,
|
|
181
|
+
и это ровно тот дефект, ради которого бандл и заводился. Регресс-кейс: транскрипт со всеми командами
|
|
182
|
+
и одним кодом в конце должен краснеть.
|
|
183
|
+
- **Бандл — пол, а не доказательство полноты.** Список команд в профиле всегда может быть неполным,
|
|
184
|
+
поэтому в профиле хранится `bundle_source` (файл + раздел, откуда гейты взяты), а воркер обязан
|
|
185
|
+
перевывести список из источника и назвать гейты, которые он прогнал сверх списка. Гейт проверяет
|
|
186
|
+
наличие `bundle_source` и этой инструкции в брифе; сама полнота остаётся человекочитаемым остатком.
|
|
187
|
+
- **Триаж-лейбл — это решение роли, а не критерии.** `classify()` в мосте называет основание вердикта
|
|
188
|
+
(секция acceptance-критериев vs лейбл), а бриф требует от воркера назвать конкретные критерии, против
|
|
189
|
+
которых он работал. Иначе «очередь валидна» и «работа проверяема» — разные утверждения.
|
|
190
|
+
- **Реальность воркспейса важнее брифового идеала.** Kanban-задача с `--project` приезжает воркеру уже
|
|
191
|
+
как git-worktree с собственной ветвью, а `gh` в окружении воркера может быть не авторизован: пуш по SSH
|
|
192
|
+
работает, PR открывает оркестратор из транскрипта. Бриф, требующий конкретного имени ветки и
|
|
193
|
+
собственноручного PR, отправляет воркера в тупик — проверено на первом же живом прогоне.
|
|
194
|
+
- **Повторный прогон команды — это тоже её результат.** Проверять все вхождения команды, а не первое:
|
|
195
|
+
«прошло, потом упало» — это упавший гейт, спрятанный за первым успехом (нашёл второй живой ревьюер).
|
|
196
|
+
И `NOT RUN` должен ловиться в любом месте блока команды, а не в первых трёх строках.
|
|
197
|
+
- **Валидатор честно объявляет свою терпимость.** Наш валидатор мягче буквы брифа по префиксу промпта
|
|
198
|
+
(`$` необязателен) и по написанию exit-кода (`exit code: 0`, `Exit=0`) — это записано как осознанная
|
|
199
|
+
терпимость, а не выдаётся за строгость: ревьюер должен видеть, где правило строгое, а где снисходительное.
|
|
200
|
+
- **Надёжность движков на ревью различается, и это часть evidence.** В одном и том же раунде
|
|
201
|
+
`harness:deepseek-v4.1-flash` (read-only) дал конформный вердикт с четырьмя MINOR, `codex` упирался в
|
|
202
|
+
квоту, `agy` дважды ушёл в таймаут на 900 с (крупная задача), `grok` дважды вернул преамбулу вместо
|
|
203
|
+
вердикта. Планировать ревью с запасным движком и фиксировать «нет вердикта» как отсутствие голоса.
|
|
204
|
+
Отдельно про agy: read-only **выполняет** команды — проверено на двух моделях (claude-sonnet-4-6:
|
|
205
|
+
точный sha256, живой timestamp, exit code 7; gemini-3.8-flash: то же плюс прогон строгого гейта с
|
|
206
|
+
`LOOP_ENGINEERING_PASS_0001` и exit 0). Значит `contract_violated` у gemini-3.8-flash означал ответ,
|
|
207
|
+
не попавший в контракт, а не неспособность. Два таймаута на 900 с дала ширина задачи: agy раскладывает
|
|
208
|
+
широкую задачу на фоновые подзадачи и уходит в ожидание до 20 минут. Лимит ywcrew на ран поднят до
|
|
209
|
+
1 800 000 мс (30 мин) и стал конфигурируемым — `defaults.timeoutMs` в `~/.ywcrew/config.json`,
|
|
210
|
+
приоритет у явного `timeoutMs` в задаче. Панель ywcrew: `harness` + `agy` + `codex` (grok выключен).
|
|
211
|
+
- **Несоответствующий output-контракту ответ члена панели = «нет вердикта», а не голос.** Один член
|
|
212
|
+
вернул преамбулу вместо вердикта; в наивном большинстве это исказило бы консенсус. Требовать
|
|
213
|
+
минимум два конформных вердикта, прежде чем объявлять консенсус.
|
|
214
|
+
|
|
215
|
+
## Два вердикта и время (этапы 7)
|
|
216
|
+
|
|
217
|
+
- **Продуктовая приёмка — отдельный вердикт с отдельным владельцем.** Техгейт отвечает «дисциплинирована
|
|
218
|
+
ли петля», продуктовый (`loopgate/gates/check_product.py`) — «может ли пользователь делать работу этим
|
|
219
|
+
продуктом». Смешав их, получаешь зелёный техгейт над продуктом, который не стартует. У каждого сценария
|
|
220
|
+
обязательна строка `why` — последствие для пользователя; сценарий без неё не принимается (D11).
|
|
221
|
+
- **Ожидания измеряются до того, как записываются.** Все сценарии sample-service сначала проверены руками
|
|
222
|
+
(200 у проб, Prometheus-текст у метрик, exit 1 с именем поля у невалидной конфигурации, ~250 мс на
|
|
223
|
+
SIGTERM) и только потом записаны. Первый же живой прогон гейта нашёл дефект в самом гейте — сценарий
|
|
224
|
+
невалидного порта перекрывался выдачей свободных портов; это и есть цена записи ожиданий заранее.
|
|
225
|
+
- **Оценку делает очередь, а не супервизор.** `loopgate/gates/estimate_task.py` считает p50/p90 по завершённым
|
|
226
|
+
ранам и предлагает `cap = max(15 мин, round_up(2 × p90))` при ≥3 измерениях; при меньшем числе ранов —
|
|
227
|
+
пол и явная пометка. Мост передаёт cap как `--max-runtime`: на cap-е диспетчер убивает воркера и
|
|
228
|
+
пере-ставит задачу, поэтому «работал вечно» — видимый отказ. Оценивать себя — работа того, чью
|
|
229
|
+
оптимизм проверяют, поэтому цифра берётся из истории, а не из обещания.
|
|
230
|
+
|
|
231
|
+
## Внешний якорь и визуальные проверки (этап 7)
|
|
232
|
+
|
|
233
|
+
- **Подписывать надо вердикт, а не файл.** Якорь `loop_gate_pass.json` на каждом прогоне получает новый
|
|
234
|
+
`generated_at`, поэтому подпись над файлом умирает на следующем прогоне. Подписывается канонический
|
|
235
|
+
payload (`marker` + `verdict_hash`) — он стабилен, пока стабилен набор вердиктов.
|
|
236
|
+
- **`ssh-keygen -Y sign` не перезаписывает существующий `.sig`** (exit 0, старая подпись остаётся на
|
|
237
|
+
месте): после смены вердикта повторная подпись молча верифицировала бы предыдущий. Удалять целевой
|
|
238
|
+
файл подписи перед подписью; регресс R24 держит это.
|
|
239
|
+
- **Свидетеля проверяет оператор, а не гейт.** Локальная проверка подписи офлайн (D12), внешняя запись
|
|
240
|
+
(комментарий в `example/sample-service#5`) — отдельный шаг `check-external`, потому что гейт не ходит в сеть.
|
|
241
|
+
Граница честности: ключ на этой же машине доказывает владение ключом, а не независимость; внешняя запись
|
|
242
|
+
делает заметной согласованную перезапись.
|
|
243
|
+
- **Визуальная проверка — не «скриншот ради скриншота».** Сценарии требуют минимум один скриншот, а
|
|
244
|
+
evaluator ловит то, чего не видит JSON: первый же vision-разбор нашёл заголовок, обрезанный посреди
|
|
245
|
+
слова без многоточия. Правило проверялось снятием фикса — иначе оно декорация.
|
|
246
|
+
- **Headless-браузер обязателен и проверяется гейтом.** Визуальный чек, который молча скипается при
|
|
247
|
+
отсутствии браузера, хуже отсутствия чека (D13 падает, если браузера нет).
|
|
248
|
+
|
|
249
|
+
## Артефакты
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
artifacts/loop_state.json состояние петли (round, phase, readiness/evidence/risk, blockers, marker)
|
|
253
|
+
artifacts/rounds/round_NN.md по одному файлу на раунд: gap → change → evidence → metrics → next
|
|
254
|
+
artifacts/loop_gate_report.json машинный вывод гейта (--report)
|
|
255
|
+
artifacts/issue_bridge_report.json что мост сделал с очередью issues (queued / needs-triage / skipped)
|
|
256
|
+
handoff/latest_loop.json маркер, статус, пути артефактов
|
|
257
|
+
handoff/supervisor_synthesis.md один синтез супервизора
|
|
258
|
+
configs/repo_profiles.json контракт репозитория-цели: бандл доказательств, PR-правила, assignee
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
`artifacts/` и `handoff/` — состояние, а не исходники: они в `.gitignore`.
|
|
262
|
+
|
|
263
|
+
## Инварианты
|
|
264
|
+
|
|
265
|
+
- Никакого заявления об успехе без воспроизводимой команды или видимого артефакта.
|
|
266
|
+
- Ремонт только по релевантным файлам; широкие переписывания запрещены.
|
|
267
|
+
- Секреты, ключи и приватные данные в артефакты не попадают.
|
|
268
|
+
- Маркер PASS печатает только гейт, не агент.
|
|
269
|
+
- Печать вердикта никогда не зависит от возможности записи: если `--report` или якорь не пишутся
|
|
270
|
+
(read-only песочница codex/agy), гейт печатает warning и всё равно выдаёт маркер — гейт, умирающий
|
|
271
|
+
после «9 ok», неотличим от сломанного.
|
|
272
|
+
- Метрики в `loop_state.json` не выдумываются: они должны согласовываться с артефактами раундов.
|
|
273
|
+
|
|
274
|
+
## Маршрутизация ролей по движкам (проверено прогонами)
|
|
275
|
+
|
|
276
|
+
| Задача | Движок | Почему |
|
|
277
|
+
|---|---|---|
|
|
278
|
+
| карта репо, чтение, ревью, команды, прогон гейта | `agy` | read-only идёт в git-worktree: команды выполняются, записи не попадают в твоё дерево; gitignored-состояние попадает в worktree через `.worktreeinclude` (`artifacts/`, `handoff/`) — проверено: гейт в worktree даёт 9 ok и PASS |
|
|
279
|
+
| прогон проектного гейта, evaluator, всё, что должно видеть реальное дерево | `codex` | нативный read-only sandbox: читает дерево целиком (включая gitignored), запись запрещена |
|
|
280
|
+
| второе мнение, независимое ревью, команды | `grok` | read-only разрешает выполнение команд, видит реальное дерево |
|
|
281
|
+
| worker по умолчанию: фичи, фиксы, тесты, правки скриптов | `harness` | DeepSeek Harness (`deepseek-v4.1-flash` через `opencode-go`), edit в git-worktree; песочницы нет — изоляцию даёт worktree |
|