vyuha 0.1.0__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.
- vyuha-0.1.0/.claude/commands/implement.md +81 -0
- vyuha-0.1.0/.claude/commands/integrate.md +23 -0
- vyuha-0.1.0/.claude/commands/next-ticket.md +7 -0
- vyuha-0.1.0/.claude/commands/orchestrate.md +392 -0
- vyuha-0.1.0/.claude/commands/tickets.md +7 -0
- vyuha-0.1.0/.github/workflows/ci.yml +63 -0
- vyuha-0.1.0/.gitignore +30 -0
- vyuha-0.1.0/.vyuha/notes/.gitkeep +0 -0
- vyuha-0.1.0/.vyuha.yml.example +65 -0
- vyuha-0.1.0/LICENSE +202 -0
- vyuha-0.1.0/NOTICE +14 -0
- vyuha-0.1.0/PKG-INFO +633 -0
- vyuha-0.1.0/README.md +385 -0
- vyuha-0.1.0/ROADMAP.md +41 -0
- vyuha-0.1.0/SECURITY.md +34 -0
- vyuha-0.1.0/ci/run_pytest.py +105 -0
- vyuha-0.1.0/ci/smoke_windows.py +156 -0
- vyuha-0.1.0/docs/ARCHITECTURE.md +202 -0
- vyuha-0.1.0/docs/assets/demo.gif +0 -0
- vyuha-0.1.0/docs/config-reference.md +213 -0
- vyuha-0.1.0/docs/demo-walkthrough.md +245 -0
- vyuha-0.1.0/docs/executor-capabilities.md +123 -0
- vyuha-0.1.0/docs/security-model.md +156 -0
- vyuha-0.1.0/docs/troubleshooting.md +281 -0
- vyuha-0.1.0/pyproject.toml +88 -0
- vyuha-0.1.0/tests/__init__.py +0 -0
- vyuha-0.1.0/tests/test_analyze.py +1024 -0
- vyuha-0.1.0/tests/test_board.py +380 -0
- vyuha-0.1.0/tests/test_claim_file.py +158 -0
- vyuha-0.1.0/tests/test_cli.py +353 -0
- vyuha-0.1.0/tests/test_concurrency.py +315 -0
- vyuha-0.1.0/tests/test_config.py +921 -0
- vyuha-0.1.0/tests/test_context_log.py +1342 -0
- vyuha-0.1.0/tests/test_create.py +308 -0
- vyuha-0.1.0/tests/test_deploy.py +202 -0
- vyuha-0.1.0/tests/test_doctor.py +303 -0
- vyuha-0.1.0/tests/test_flags.py +217 -0
- vyuha-0.1.0/tests/test_lifecycle.py +1839 -0
- vyuha-0.1.0/tests/test_orchestrate.py +3539 -0
- vyuha-0.1.0/tests/test_pidutil.py +43 -0
- vyuha-0.1.0/tests/test_projects.py +326 -0
- vyuha-0.1.0/tests/test_promote.py +134 -0
- vyuha-0.1.0/tests/test_prompts.py +503 -0
- vyuha-0.1.0/tests/test_reviewer.py +444 -0
- vyuha-0.1.0/tests/test_safeguards.py +582 -0
- vyuha-0.1.0/tests/test_ticket.py +411 -0
- vyuha-0.1.0/tests/test_watch.py +371 -0
- vyuha-0.1.0/tests/test_worktree.py +59 -0
- vyuha-0.1.0/vyuha/__init__.py +4 -0
- vyuha-0.1.0/vyuha/analyze.py +842 -0
- vyuha-0.1.0/vyuha/board.py +637 -0
- vyuha-0.1.0/vyuha/claim_file.py +96 -0
- vyuha-0.1.0/vyuha/cli.py +1035 -0
- vyuha-0.1.0/vyuha/companion.py +74 -0
- vyuha-0.1.0/vyuha/concurrency.py +224 -0
- vyuha-0.1.0/vyuha/config.py +768 -0
- vyuha-0.1.0/vyuha/context_log.py +917 -0
- vyuha-0.1.0/vyuha/create.py +150 -0
- vyuha-0.1.0/vyuha/deploy.py +130 -0
- vyuha-0.1.0/vyuha/doctor.py +190 -0
- vyuha-0.1.0/vyuha/executor.py +146 -0
- vyuha-0.1.0/vyuha/flags.py +131 -0
- vyuha-0.1.0/vyuha/ghsync.py +121 -0
- vyuha-0.1.0/vyuha/lifecycle.py +1207 -0
- vyuha-0.1.0/vyuha/mcp.py +369 -0
- vyuha-0.1.0/vyuha/orchestrate.py +1890 -0
- vyuha-0.1.0/vyuha/pidutil.py +84 -0
- vyuha-0.1.0/vyuha/projects.py +81 -0
- vyuha-0.1.0/vyuha/promote.py +160 -0
- vyuha-0.1.0/vyuha/prompts.py +117 -0
- vyuha-0.1.0/vyuha/reviewer.py +188 -0
- vyuha-0.1.0/vyuha/safeguards.py +162 -0
- vyuha-0.1.0/vyuha/skills/implement.md +81 -0
- vyuha-0.1.0/vyuha/skills/integrate.md +23 -0
- vyuha-0.1.0/vyuha/skills/next-ticket.md +7 -0
- vyuha-0.1.0/vyuha/skills/orchestrate.md +401 -0
- vyuha-0.1.0/vyuha/skills/tickets.md +7 -0
- vyuha-0.1.0/vyuha/stats.py +123 -0
- vyuha-0.1.0/vyuha/templates/__init__.py +0 -0
- vyuha-0.1.0/vyuha/templates/prompts/__init__.py +0 -0
- vyuha-0.1.0/vyuha/templates/prompts/analyze.md +29 -0
- vyuha-0.1.0/vyuha/templates/prompts/implement.md +1 -0
- vyuha-0.1.0/vyuha/templates/prompts/review.md +1 -0
- vyuha-0.1.0/vyuha/templates/scripts/__init__.py +0 -0
- vyuha-0.1.0/vyuha/templates/scripts/pre-deploy-check.sh +16 -0
- vyuha-0.1.0/vyuha/templates/scripts/run-tests.sh +14 -0
- vyuha-0.1.0/vyuha/ticket.py +229 -0
- vyuha-0.1.0/vyuha/watch.py +219 -0
- vyuha-0.1.0/vyuha/worktree.py +104 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# /implement — Start a Ticket
|
|
2
|
+
|
|
3
|
+
Claims a ticket and creates its git worktree.
|
|
4
|
+
|
|
5
|
+
**Usage:** `/implement TICK-NNN`
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
vyuha start $ARGUMENTS
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
After the worktree is created (`worktrees/<ticket-id>/`), edit files **there** — never edit the main checkout's copies. Reset CWD between tickets.
|
|
12
|
+
|
|
13
|
+
Run tests from within the worktree (use relative paths to your test runner). When tests are green:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
vyuha complete $ARGUMENTS
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then merge:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
vyuha merge $ARGUMENTS
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Writing agent notes
|
|
28
|
+
|
|
29
|
+
After completing a ticket (tests green, `vyuha complete` called), write a short notes block for each file in `ticket.touches` so future agents inherit constraints discovered during this implementation.
|
|
30
|
+
|
|
31
|
+
### Note file location
|
|
32
|
+
|
|
33
|
+
For each file path in `ticket.touches`, the note file is:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
.vyuha/notes/<flat_path>.md
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
where `<flat_path>` is the file path with every `/` replaced by `_`.
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
- `src/vyuha/core.py` → `.vyuha/notes/src_vyuha_core.py.md`
|
|
43
|
+
- `.claude/commands/implement.md` → `.vyuha/notes/.claude_commands_implement.md.md`
|
|
44
|
+
|
|
45
|
+
### How to write notes
|
|
46
|
+
|
|
47
|
+
**Append** a tagged block to the note file (create the file if it doesn't exist):
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
## [TICK-NNN]
|
|
51
|
+
- <factual constraint 1>
|
|
52
|
+
- <factual constraint 2>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Rules:
|
|
56
|
+
- Write **factual constraints only**: "X fails if Y", "always do Z before W", "field X must be non-null or parser crashes"
|
|
57
|
+
- **Max 5 bullets per ticket per file** — be terse
|
|
58
|
+
- If nothing non-obvious was learned about a file, **write nothing** (omit the block entirely for that file)
|
|
59
|
+
- Do not repeat what the ticket spec already says; write only what surprised you or would trip up the next agent
|
|
60
|
+
|
|
61
|
+
### Escalation rule
|
|
62
|
+
|
|
63
|
+
| Type of learning | Where it goes |
|
|
64
|
+
|---|---|
|
|
65
|
+
| File-specific or ephemeral (implementation detail, edge case in one file) | `.vyuha/notes/<flat_path>.md` only |
|
|
66
|
+
| Project-wide convention discovered during implementation | Append a proposal to `.vyuha/pending-globals.md` (also gitignored) with the ticket ID and rationale |
|
|
67
|
+
|
|
68
|
+
**Never write to `CLAUDE.md` autonomously.** Only a human may promote a proposal from `pending-globals.md` into `CLAUDE.md`.
|
|
69
|
+
|
|
70
|
+
### Example
|
|
71
|
+
|
|
72
|
+
After implementing TICK-042 that touched `src/vyuha/concurrency.py` and `.vyuha.yml`:
|
|
73
|
+
|
|
74
|
+
`.vyuha/notes/src_vyuha_concurrency.py.md`:
|
|
75
|
+
```markdown
|
|
76
|
+
## [TICK-042]
|
|
77
|
+
- flock path must be on the same filesystem as the repo root or acquire silently fails
|
|
78
|
+
- always release lock in a trap, not in normal control flow
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`.vyuha/notes/.vyuha.yml.md` — omitted (nothing non-obvious learned about the YAML schema)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# /integrate — Merge and Promote
|
|
2
|
+
|
|
3
|
+
Integration ritual for code-complete tickets.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# 1. Review what's code_complete
|
|
7
|
+
vyuha board
|
|
8
|
+
|
|
9
|
+
# 2. For each code_complete ticket:
|
|
10
|
+
vyuha review TICK-NNN
|
|
11
|
+
vyuha merge TICK-NNN
|
|
12
|
+
|
|
13
|
+
# 3. Check pipeline status
|
|
14
|
+
vyuha pipeline-status
|
|
15
|
+
|
|
16
|
+
# 4. Promote to an environment (manual envs only):
|
|
17
|
+
vyuha promote <env-name>
|
|
18
|
+
|
|
19
|
+
# 5. If feature-flagged, enable in the target environment:
|
|
20
|
+
vyuha flag enable <flag-name> --env <env-name>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For auto-trigger environments (hook-driven), the pipeline handles promotion automatically.
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
# /orchestrate — Clear the Board
|
|
2
|
+
|
|
3
|
+
Runs the ticket-processing loop. Pulls touch-disjoint batches from `vyuha next`,
|
|
4
|
+
takes each ticket as far as policy allows **in parallel up to a resource cap**,
|
|
5
|
+
and repeats until no eligible tickets remain.
|
|
6
|
+
|
|
7
|
+
**Usage:** `/orchestrate [--max N] [--dry-run] [--human-review final|per_ticket|none]`
|
|
8
|
+
|
|
9
|
+
This skill coordinates the run. It never edits code itself — it dispatches per-ticket
|
|
10
|
+
subagents and runs the CLI verbs between them.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 0. Preflight — single-orchestrator guard
|
|
15
|
+
|
|
16
|
+
Only one orchestrator may run per repo. A second one would double the live agent count
|
|
17
|
+
and blow past the rate-limit / GPU cap (each orchestrator enforces `max_parallel`
|
|
18
|
+
independently, with no cross-process coordination).
|
|
19
|
+
|
|
20
|
+
The check-and-set, stale-lock reclaim, and atomicity (`flock`) live in `concurrency.py`;
|
|
21
|
+
the skill calls the verb with its own shell PID (`$$`):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
vyuha orchestrator-lock acquire --pid $$ || exit 1 # refuses if a live orchestrator holds it
|
|
25
|
+
trap 'vyuha orchestrator-lock release --pid $$' EXIT # release on exit/error/interrupt
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
> A stale lock (holder process dead) is reclaimed automatically. To inspect without
|
|
29
|
+
> claiming, run `vyuha orchestrator-lock status`; to override a wedged lock, add `--force`.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 1. Resolve the concurrency cap
|
|
34
|
+
|
|
35
|
+
The cap is the **resource gate**; `vyuha next` is the **correctness gate**. The
|
|
36
|
+
effective batch size each round is `min(eligible_disjoint_tickets, max_parallel)`.
|
|
37
|
+
|
|
38
|
+
Resolve `max_parallel` in this order (first hit wins):
|
|
39
|
+
|
|
40
|
+
1. `--max N` flag on this invocation.
|
|
41
|
+
2. `executors.<executor>.max_parallel` in `.vyuha.yml` for the active `executor`.
|
|
42
|
+
3. Top-level `max_parallel` in `.vyuha.yml`.
|
|
43
|
+
4. Built-in default: **2**.
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
# .vyuha.yml
|
|
47
|
+
executor: claude
|
|
48
|
+
max_parallel: 2 # global default
|
|
49
|
+
executors:
|
|
50
|
+
claude: { max_parallel: 3 } # cloud → bounded by API rate limits
|
|
51
|
+
local: { max_parallel: 1 } # single-GPU → effectively sequential
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Reminder on semantics: the executor value is an **override**, not additive.
|
|
55
|
+
`local: 1` runs one ticket at a time on purpose; raise it only when your local server
|
|
56
|
+
batches (e.g. vLLM continuous batching).
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 2. The loop
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
repeat:
|
|
64
|
+
batch_json = `vyuha --json next`
|
|
65
|
+
candidates = [batch_json.next] + batch_json.peers # already touch-disjoint
|
|
66
|
+
if candidates is empty: break # no eligible tickets remain
|
|
67
|
+
batch = candidates[:max_parallel] # apply resource cap
|
|
68
|
+
run every ticket in `batch` IN PARALLEL (one subagent each)
|
|
69
|
+
collect finished tickets; as a slot frees, the loop's next round refills it
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Use a **worker-pool / semaphore**, not blind fan-out: keep at most `max_parallel`
|
|
73
|
+
subagents live at once and pull the next candidate as each finishes. Do **not** spawn the
|
|
74
|
+
whole board and wait. `vyuha next` only ever returns a disjoint set, so re-querying
|
|
75
|
+
each round naturally respects the `touches` lock as tickets enter/leave flight.
|
|
76
|
+
|
|
77
|
+
### Prior agent notes injection
|
|
78
|
+
|
|
79
|
+
Before dispatching the subagent for a ticket, collect any notes left by previous agents
|
|
80
|
+
for files this ticket will touch.
|
|
81
|
+
|
|
82
|
+
**Step 1 — collect notes**
|
|
83
|
+
|
|
84
|
+
For each path in `ticket.touches`, derive the flat filename:
|
|
85
|
+
- Replace every `/` in the path with `_`
|
|
86
|
+
- Read `.vyuha/notes/<flat_path>.md` if it exists; silently skip if missing
|
|
87
|
+
|
|
88
|
+
**Step 2 — build the injection**
|
|
89
|
+
|
|
90
|
+
Concatenate all non-empty note file contents. If the result is non-empty, prepend it to
|
|
91
|
+
the agent prompt as a dedicated section:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
## Prior agent notes
|
|
95
|
+
|
|
96
|
+
<concatenated note file contents>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
If no note files exist (or all are empty), **omit the section entirely** — do not add a
|
|
100
|
+
heading with no content.
|
|
101
|
+
|
|
102
|
+
**Step 3 — dispatch**
|
|
103
|
+
|
|
104
|
+
Pass the (possibly augmented) prompt to the subagent. The injection is executor-agnostic:
|
|
105
|
+
it works the same for `claude-subagent`, `claude-process`, `aider`, and any other
|
|
106
|
+
executor in the dispatch table below.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### Per-ticket executor dispatch
|
|
111
|
+
|
|
112
|
+
The executor for each ticket is resolved per-ticket:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
executor = ticket.get('executor') or cfg['executor']
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| `executor` value | Dispatch method |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `claude-subagent` (or `claude`) | Task tool (in-process subagent) |
|
|
121
|
+
| `claude-process` | `claude -p <prompt>` subprocess in the ticket's worktree |
|
|
122
|
+
| `aider`, `codex`, `<cmd>` | configured agent as a process in the ticket's worktree |
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
vyuha start TICK-NNN # claim + worktree (TOCTOU-safe; see below)
|
|
126
|
+
→ dispatch per executor (see table above) with prior notes prepended
|
|
127
|
+
vyuha complete TICK-NNN # drift check vs touches + status → code_complete
|
|
128
|
+
→ review gate: see §3 below
|
|
129
|
+
vyuha merge TICK-NNN # ff merge → main, worktree removed
|
|
130
|
+
→ analytics log: see §2a below (MANDATORY — do not skip)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### §2a. Analytics logging (mandatory after every merge)
|
|
134
|
+
|
|
135
|
+
Immediately after `vyuha merge TICK-NNN` succeeds, call `vyuha log` with the token
|
|
136
|
+
counts from the task-notification `<usage>` blocks. Do not skip this step — missing
|
|
137
|
+
entries cannot be backfilled accurately later.
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
vyuha log TICK-NNN \
|
|
141
|
+
--tokens <total_subagent_tokens> # sum of implementer + all reviewer usage blocks
|
|
142
|
+
--summary-tokens <summary_tok> # rough: len(agent_result_text) // 4
|
|
143
|
+
--executor claude-subagent \
|
|
144
|
+
--model claude-sonnet-4-6 \
|
|
145
|
+
--tests-passed # include if tests passed in the subagent
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Where to get the numbers:**
|
|
149
|
+
- `total_subagent_tokens`: sum the `subagent_tokens` field from every task-notification
|
|
150
|
+
`<usage>` block for this ticket (implementer + reviewer(s))
|
|
151
|
+
- `summary_tokens`: rough estimate — `len(result_text) // 4` where `result_text` is the
|
|
152
|
+
agent's returned summary string
|
|
153
|
+
|
|
154
|
+
Log immediately; if the merge was done manually (without `vyuha merge`), still call
|
|
155
|
+
`vyuha log` right after the git commit.
|
|
156
|
+
|
|
157
|
+
If `vyuha start` fails with *"grabbed by another session"* or *"conflicts with …"*,
|
|
158
|
+
**skip that ticket this round** and move on — it is not an error, just a lost race or a
|
|
159
|
+
lock. The next `vyuha next` will reflect reality.
|
|
160
|
+
|
|
161
|
+
For `claude-subagent`/`claude`: Give the subagent only the ticket spec and its `touches` —
|
|
162
|
+
not the board state.
|
|
163
|
+
|
|
164
|
+
For `claude-process`: Run `claude -p "<prompt>"` as a subprocess in the ticket's worktree
|
|
165
|
+
(`worktrees/tick-NNN`). The prompt is the same context as what would be given to a Task
|
|
166
|
+
subagent. Capture stdout/stderr; non-zero exit means implementation failed.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 3. Review gate (always runs — agent review at full quality)
|
|
171
|
+
|
|
172
|
+
After `vyuha complete TICK-NNN`, dispatch a **review subagent** using the resolved
|
|
173
|
+
reviewer executor — which may differ from the implementer's executor. Resolve it as:
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
reviewer = ticket.get('reviewer') or cfg.get('reviewer') or cfg['executor']
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Resolution order (first non-empty value wins):
|
|
180
|
+
1. Ticket-level `reviewer` field in frontmatter (per-ticket override)
|
|
181
|
+
2. Project-level `reviewer` in `.vyuha.yml` (project-wide reviewer default)
|
|
182
|
+
3. Project-level `executor` in `.vyuha.yml` (fallback to implementer's executor)
|
|
183
|
+
|
|
184
|
+
Dispatch the review subagent using the resolved `reviewer` value, **not** the
|
|
185
|
+
implementer's executor. This allows, for example, a `claude-process` project to use
|
|
186
|
+
`claude-subagent` for reviews, or a specific ticket to route its review to `human`.
|
|
187
|
+
|
|
188
|
+
### What to give the reviewer
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
Ticket spec:
|
|
192
|
+
id: TICK-NNN
|
|
193
|
+
title: <title>
|
|
194
|
+
close_criteria: <close_criteria>
|
|
195
|
+
touches: [<files>]
|
|
196
|
+
|
|
197
|
+
Diff (branch vs main):
|
|
198
|
+
<output of: git diff main...tick-nnn>
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### What the reviewer returns
|
|
202
|
+
|
|
203
|
+
A JSON object on stdout:
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"verdict": "approved" | "changes_requested",
|
|
207
|
+
"summary": "<one-line verdict summary>",
|
|
208
|
+
"findings": ["<finding 1>", "<finding 2>"]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### How the orchestrator calls the CLI
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Parse the JSON verdict and call:
|
|
216
|
+
vyuha review TICK-NNN \
|
|
217
|
+
--verdict <verdict> \
|
|
218
|
+
--summary "<summary>" \
|
|
219
|
+
--findings "<newline-joined findings>"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`vyuha review --verdict approved` → flips ticket to `in_review`, stores
|
|
223
|
+
`review_verdict` and `review_summary` in frontmatter, appends findings under
|
|
224
|
+
`## Review Findings` in the ticket body.
|
|
225
|
+
|
|
226
|
+
`vyuha review --verdict changes_requested` → stores fields, exits non-zero,
|
|
227
|
+
**leaves ticket at `code_complete`**. The orchestrator treats this ticket as
|
|
228
|
+
blocked for that batch round.
|
|
229
|
+
|
|
230
|
+
### Policy on changes_requested
|
|
231
|
+
|
|
232
|
+
- Leave the ticket at `code_complete` with the findings stored.
|
|
233
|
+
- Continue the rest of the batch — do not halt the whole run.
|
|
234
|
+
- Report blocked tickets in the end-of-run summary (§6).
|
|
235
|
+
- Do **not** auto-retry the implementation; a human should inspect first.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 4. Rate-limit / load handling (set the cap optimistically)
|
|
240
|
+
<!-- was §3 before the review gate was added as §3 -->
|
|
241
|
+
|
|
242
|
+
So you can set `max_parallel` to the cap rather than guessing low:
|
|
243
|
+
|
|
244
|
+
- On HTTP **429 / rate-limit** from a subagent: respect `Retry-After`, exponential
|
|
245
|
+
backoff with jitter, then retry that ticket. Do **not** fail the batch.
|
|
246
|
+
- If 429s persist across a round, **shrink the live worker count by 1** (adaptive
|
|
247
|
+
throttle) and log it; recover upward after a clean round.
|
|
248
|
+
- On a **local** executor, OOM/timeout is the signal instead — lower the cap, never retry
|
|
249
|
+
blindly into the same wall.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 5. Review policy (human gates only — agent review ALWAYS runs)
|
|
254
|
+
|
|
255
|
+
Set per-batch via `--human-review`, default `final`. Agent review at full quality runs on
|
|
256
|
+
**every** ticket regardless.
|
|
257
|
+
|
|
258
|
+
| Mode | Agent review | Human gate |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| `per_ticket` | full quality | stop each ticket at `in_review` for a human |
|
|
261
|
+
| `final` (default) | full quality | one human pass over the whole batch at the end |
|
|
262
|
+
| `none` | full quality | none — pre-vetted/trusted runs only |
|
|
263
|
+
|
|
264
|
+
`none` does not mean unreviewed; it means no *human* gate.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## 5a. Watch daemon spawn on exit (TICK-019 / TICK-023)
|
|
269
|
+
|
|
270
|
+
After the run loop exits — whether the board is empty or only blocked/in-review
|
|
271
|
+
tickets remain — check for tickets still at `in_review` status.
|
|
272
|
+
|
|
273
|
+
**If any `in_review` tickets exist:**
|
|
274
|
+
|
|
275
|
+
1. Check whether a watcher is already running:
|
|
276
|
+
```bash
|
|
277
|
+
vyuha watch --status
|
|
278
|
+
```
|
|
279
|
+
2. If not running, spawn a detached background watcher using the `spawn_detached`
|
|
280
|
+
helper from `vyuha.lifecycle`. This is platform-agnostic Python — no `nohup`,
|
|
281
|
+
no `&`, no shell syntax:
|
|
282
|
+
```python
|
|
283
|
+
from vyuha.lifecycle import spawn_detached
|
|
284
|
+
from pathlib import Path
|
|
285
|
+
import sys
|
|
286
|
+
|
|
287
|
+
log_path = repo_root / ".vyuha" / "watch.log"
|
|
288
|
+
pid = spawn_detached([sys.executable, "-m", "vyuha", "watch"], log_path)
|
|
289
|
+
print(f"[orchestrate] watch daemon started (pid {pid}) — will merge approved PRs in background")
|
|
290
|
+
```
|
|
291
|
+
The orchestrator's `spawn_watch_daemon(repo_root)` function wraps this exactly.
|
|
292
|
+
3. If already running, skip spawn and note it in the summary.
|
|
293
|
+
|
|
294
|
+
**End-of-run summary line (when daemon spawned or already running):**
|
|
295
|
+
```
|
|
296
|
+
[waiting] N ticket(s) in review — watch daemon running (pid NNN)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**If no `in_review` tickets:** do not spawn the watcher.
|
|
300
|
+
|
|
301
|
+
The watcher polls every 60 seconds, calls `vyuha merge` on APPROVED PRs, and exits
|
|
302
|
+
when no `in_review` tickets with a `pr_number` remain.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 7. Context hygiene — keep the main session slim
|
|
307
|
+
|
|
308
|
+
The orchestrator runs in the main Claude session. Every tool result that lands here
|
|
309
|
+
consumes context that is never reclaimed. Subagents exist precisely to keep heavy
|
|
310
|
+
content out of this session. Violating these rules causes the main context to fill
|
|
311
|
+
faster than the board clears, eventually forcing a summarisation mid-run.
|
|
312
|
+
|
|
313
|
+
### Rules
|
|
314
|
+
|
|
315
|
+
**`vyuha start` — suppress the ticket echo**
|
|
316
|
+
|
|
317
|
+
`vyuha start` prints the full ticket spec to stdout. Capture only the first 3 lines
|
|
318
|
+
(worktree path / branch / "Started"):
|
|
319
|
+
|
|
320
|
+
```bash
|
|
321
|
+
vyuha start TICK-NNN 2>&1 | head -3
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Reviewer subagents — never relay diffs through main context**
|
|
325
|
+
|
|
326
|
+
Do NOT fetch `git diff main...tick-NNN` in the main session and paste it into the
|
|
327
|
+
reviewer prompt. Instead, tell the reviewer where to look:
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
Read the diff yourself:
|
|
331
|
+
git -C /home/.../worktrees/tick-NNN diff main...tick-NNN
|
|
332
|
+
Do not ask me to provide it — run the command directly.
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
This keeps the diff bytes in the subagent's context, not here.
|
|
336
|
+
|
|
337
|
+
**Reviewer subagents — read worktree files, not main-repo files**
|
|
338
|
+
|
|
339
|
+
Reviewer agents that need to read source files MUST be told the worktree path
|
|
340
|
+
explicitly. Agents that default to the main repo see pre-merge state and produce
|
|
341
|
+
false `changes_requested` verdicts. Always include in every reviewer prompt:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
Work from the worktree at: /home/.../worktrees/tick-NNN
|
|
345
|
+
Do NOT read files from the main repo path.
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Notes files — bounded by design**
|
|
349
|
+
|
|
350
|
+
Notes files are written only on ticket hibernation (interrupted executor sessions).
|
|
351
|
+
Each hibernation overwrites the file (not appends), and diffs are truncated at 30 KB
|
|
352
|
+
at write time. Notes files are therefore always small and safe to inject as-is.
|
|
353
|
+
|
|
354
|
+
**`vyuha next` / `vyuha board` — use sparingly**
|
|
355
|
+
|
|
356
|
+
Call `vyuha --json next` once per loop iteration. Do not call `vyuha board` mid-loop
|
|
357
|
+
unless diagnosing a problem — the JSON output from `next` has everything needed.
|
|
358
|
+
|
|
359
|
+
### Symptoms of drift
|
|
360
|
+
|
|
361
|
+
If the main context is above ~40 % mid-run, check:
|
|
362
|
+
1. Were recent `vyuha start` outputs unsuppressed?
|
|
363
|
+
2. Did a reviewer prompt contain an inline diff?
|
|
364
|
+
3. Was a large notes file read into context?
|
|
365
|
+
|
|
366
|
+
Correct forward; do not restart the run.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## 6. Stop conditions
|
|
371
|
+
|
|
372
|
+
- `vyuha next` returns no candidates → **no eligible tickets remain**, exit cleanly.
|
|
373
|
+
- Any ticket fails review (`changes_requested`) → leave at `code_complete` (§3), continue
|
|
374
|
+
rest, report blocked tickets at the end.
|
|
375
|
+
- Any ticket with `--human-review per_ticket` approved → stops at `in_review` for a human.
|
|
376
|
+
- Interrupt / error → release the orchestrator lock (§0 trap) and report what merged,
|
|
377
|
+
what is in flight, and what remains.
|
|
378
|
+
|
|
379
|
+
End every run with a summary: merged, blocked/in-review, skipped (lost races), remaining.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Notes / open design points
|
|
384
|
+
|
|
385
|
+
- **`vyuha next` returns one disjoint batch, not a `--limit`.** The skill applies the
|
|
386
|
+
`max_parallel` slice itself. A future `vyuha next --limit N` would let the CLI enforce
|
|
387
|
+
the cap (testable without an LLM in the loop).
|
|
388
|
+
- **The single-orchestrator lock is advisory** (PID file, §0) but now hardened in
|
|
389
|
+
`concurrency.py` (TICK-009 / DECISIONS F14): atomic check-and-set under an `flock`, with
|
|
390
|
+
stale-lock reclaim, exposed via `vyuha orchestrator-lock`. The per-ticket claim also
|
|
391
|
+
takes an `flock` around its read→write→commit window (`claim_lock`), closing the residual
|
|
392
|
+
TOCTOU gap — git's `index.lock` only serialized the status commit, not the whole claim.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["**"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
17
|
+
python-version: ["3.11"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v5
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: pip install -e .[dev]
|
|
29
|
+
|
|
30
|
+
- name: Run tests
|
|
31
|
+
if: runner.os != 'Windows'
|
|
32
|
+
run: python ci/run_pytest.py
|
|
33
|
+
|
|
34
|
+
- name: Run tests
|
|
35
|
+
if: runner.os == 'Windows'
|
|
36
|
+
shell: cmd
|
|
37
|
+
run: python ci/run_pytest.py
|
|
38
|
+
|
|
39
|
+
smoke-windows:
|
|
40
|
+
name: Windows runtime smoke (Python ${{ matrix.python-version }})
|
|
41
|
+
runs-on: windows-latest
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
python-version: ["3.11"]
|
|
46
|
+
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v5
|
|
49
|
+
|
|
50
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
51
|
+
uses: actions/setup-python@v6
|
|
52
|
+
with:
|
|
53
|
+
python-version: ${{ matrix.python-version }}
|
|
54
|
+
|
|
55
|
+
- name: Install dependencies
|
|
56
|
+
run: pip install -e .[dev]
|
|
57
|
+
|
|
58
|
+
# Unmocked end-to-end exercise of the OS boundaries the unit suite mocks/skips:
|
|
59
|
+
# detached spawn (CREATE_NEW_PROCESS_GROUP), os.kill SIGTERM (TerminateProcess),
|
|
60
|
+
# the os.kill liveness probe, portalocker flock, and a real `vyuha init` run.
|
|
61
|
+
- name: Run runtime smoke test
|
|
62
|
+
shell: cmd
|
|
63
|
+
run: python ci/smoke_windows.py
|
vyuha-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
venv/
|
|
9
|
+
.venv/
|
|
10
|
+
|
|
11
|
+
# Vyuha local state
|
|
12
|
+
.vyuha.yml
|
|
13
|
+
.vyuha/*
|
|
14
|
+
!.vyuha/notes/
|
|
15
|
+
.vyuha/notes/*
|
|
16
|
+
!.vyuha/notes/.gitkeep
|
|
17
|
+
worktrees/
|
|
18
|
+
*-context-log.jsonl
|
|
19
|
+
|
|
20
|
+
# Claude Code harness files
|
|
21
|
+
HANDOFF.md
|
|
22
|
+
next-session-prompt.md
|
|
23
|
+
AUTONOMOUS_SDLC_PLATFORM_REVIEW.md
|
|
24
|
+
TICK-017-complete.txt
|
|
25
|
+
.claude/auto-continue.md
|
|
26
|
+
.claude/settings.local.json
|
|
27
|
+
.claude/settings.example.json
|
|
28
|
+
.claude/standing-instructions.md
|
|
29
|
+
.claude/stop-failure-events.jsonl
|
|
30
|
+
.claude/statusline-quota-cache.sh
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# .vyuha.yml — project configuration for vyuha
|
|
2
|
+
# Copy this file to .vyuha.yml in your repo root and edit to taste.
|
|
3
|
+
# Or run `vyuha init` to generate a config interactively.
|
|
4
|
+
|
|
5
|
+
ticket_prefix: TICK # TICK-NNN, FEAT-NNN, BUG-NNN, TASK-NNN, etc.
|
|
6
|
+
tickets_dir: .vyuha/tickets
|
|
7
|
+
worktrees_dir: .vyuha/worktrees
|
|
8
|
+
|
|
9
|
+
# Who writes the code
|
|
10
|
+
executor: claude # claude | claude-subagent | claude-process | aider | openhands | codex | ollama
|
|
11
|
+
|
|
12
|
+
# Who reviews the code (optional — defaults to executor if omitted).
|
|
13
|
+
# Set to a different executor for model review, or `human` to pause after
|
|
14
|
+
# completion until a person records an explicit verdict.
|
|
15
|
+
# reviewer: claude
|
|
16
|
+
|
|
17
|
+
# Max tickets the orchestrator runs in parallel.
|
|
18
|
+
# Effective batch = min(touch-disjoint candidates, max_parallel)
|
|
19
|
+
max_parallel: 2
|
|
20
|
+
|
|
21
|
+
# Per-executor overrides (replace the global, not additive).
|
|
22
|
+
# `flags` are prepended to the executor command when dispatching a ticket.
|
|
23
|
+
# The flags below enable headless/autonomous operation — remove them if you
|
|
24
|
+
# want the executor to prompt for approval on each action (supervised mode).
|
|
25
|
+
executors:
|
|
26
|
+
claude: { max_parallel: 3, flags: ["--dangerously-skip-permissions"] }
|
|
27
|
+
aider: { max_parallel: 1, flags: ["--yes-always"] }
|
|
28
|
+
codex: { max_parallel: 3, flags: ["--approval-policy=never"] }
|
|
29
|
+
ollama: { max_parallel: 1, models: { implement: llama3.1, review: qwen2.5-coder } }
|
|
30
|
+
|
|
31
|
+
# Files/patterns where overlapping tickets cannot run in parallel.
|
|
32
|
+
# Leave empty if your project has no shared core files.
|
|
33
|
+
core_files: []
|
|
34
|
+
core_patterns: []
|
|
35
|
+
|
|
36
|
+
# Statuses during which a ticket's `touches` stay locked (held until merge).
|
|
37
|
+
lock_statuses: [in_progress, code_complete, in_review]
|
|
38
|
+
|
|
39
|
+
# ── Feature flags (optional) ─────────────────────────────────────────────────
|
|
40
|
+
# Remove this section if you do not use feature flags.
|
|
41
|
+
flag_file: ~/.vyuha/feature_flags.json
|
|
42
|
+
|
|
43
|
+
# ── Deployment pipeline (optional) ───────────────────────────────────────────
|
|
44
|
+
# Remove this section for projects with no deployment pipeline (libraries,
|
|
45
|
+
# research, open-source). Add one entry per environment in promotion order.
|
|
46
|
+
environments:
|
|
47
|
+
- name: staging
|
|
48
|
+
branch: staging
|
|
49
|
+
from: main # base branch for pending-diff + promotion
|
|
50
|
+
trigger: manual # manual | auto (auto = hook-driven, vyuha reports only)
|
|
51
|
+
sync: ff-only # ff-only | merge-no-ff
|
|
52
|
+
guard_script: null # path to script; exit 1 blocks promote
|
|
53
|
+
pre_promote: [] # scripts to run before promote
|
|
54
|
+
post_promote: [] # scripts to run after promote
|
|
55
|
+
flag_file: ~/.vyuha/staging.feature_flags.json
|
|
56
|
+
|
|
57
|
+
- name: production
|
|
58
|
+
branch: production
|
|
59
|
+
from: main
|
|
60
|
+
trigger: manual
|
|
61
|
+
sync: ff-only
|
|
62
|
+
guard_script: null
|
|
63
|
+
pre_promote: []
|
|
64
|
+
post_promote: []
|
|
65
|
+
flag_file: ~/.vyuha/feature_flags.json
|