deviatdd 2.5.1__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.
- deviatdd-2.5.1.dist-info/METADATA +386 -0
- deviatdd-2.5.1.dist-info/RECORD +124 -0
- deviatdd-2.5.1.dist-info/WHEEL +4 -0
- deviatdd-2.5.1.dist-info/entry_points.txt +2 -0
- deviatdd-2.5.1.dist-info/licenses/LICENSE +21 -0
- deviate/__init__.py +3 -0
- deviate/cli/__init__.py +824 -0
- deviate/cli/_common.py +155 -0
- deviate/cli/adhoc.py +183 -0
- deviate/cli/constitution.py +113 -0
- deviate/cli/feature.py +94 -0
- deviate/cli/init.py +485 -0
- deviate/cli/inspect.py +208 -0
- deviate/cli/macro.py +937 -0
- deviate/cli/meso.py +1894 -0
- deviate/cli/micro.py +3249 -0
- deviate/cli/review.py +441 -0
- deviate/core/__init__.py +0 -0
- deviate/core/_shared.py +20 -0
- deviate/core/agent.py +505 -0
- deviate/core/cache_discipline.py +65 -0
- deviate/core/commands.py +202 -0
- deviate/core/commit.py +86 -0
- deviate/core/complexity.py +36 -0
- deviate/core/constitution.py +85 -0
- deviate/core/contract.py +17 -0
- deviate/core/convention.py +147 -0
- deviate/core/epic.py +73 -0
- deviate/core/issues.py +46 -0
- deviate/core/prd.py +18 -0
- deviate/core/profile.py +33 -0
- deviate/core/repo.py +47 -0
- deviate/core/run_logger.py +50 -0
- deviate/core/tasks_ledger.py +69 -0
- deviate/core/treesitter/__init__.py +31 -0
- deviate/core/treesitter/analysis.py +457 -0
- deviate/core/treesitter/models.py +35 -0
- deviate/core/treesitter/parser.py +184 -0
- deviate/core/treesitter/queries/bash.scm +7 -0
- deviate/core/treesitter/queries/c_sharp.scm +11 -0
- deviate/core/treesitter/queries/cpp.scm +9 -0
- deviate/core/treesitter/queries/css.scm +4 -0
- deviate/core/treesitter/queries/dockerfile.scm +8 -0
- deviate/core/treesitter/queries/elixir.scm +6 -0
- deviate/core/treesitter/queries/go.scm +9 -0
- deviate/core/treesitter/queries/hcl.scm +3 -0
- deviate/core/treesitter/queries/html.scm +3 -0
- deviate/core/treesitter/queries/javascript.scm +12 -0
- deviate/core/treesitter/queries/json.scm +4 -0
- deviate/core/treesitter/queries/kotlin.scm +8 -0
- deviate/core/treesitter/queries/markdown.scm +4 -0
- deviate/core/treesitter/queries/python.scm +10 -0
- deviate/core/treesitter/queries/rust.scm +12 -0
- deviate/core/treesitter/queries/sql.scm +7 -0
- deviate/core/treesitter/queries/swift.scm +10 -0
- deviate/core/treesitter/queries/toml.scm +3 -0
- deviate/core/treesitter/queries/tsx.scm +14 -0
- deviate/core/treesitter/queries/typescript.scm +14 -0
- deviate/core/treesitter/queries/yaml.scm +4 -0
- deviate/core/validation.py +153 -0
- deviate/core/worktree.py +141 -0
- deviate/main.py +4 -0
- deviate/prompts/__init__.py +0 -0
- deviate/prompts/assembly.py +122 -0
- deviate/prompts/auto/__init__.py +1 -0
- deviate/prompts/auto/execute.md +62 -0
- deviate/prompts/auto/explore.md +103 -0
- deviate/prompts/auto/green.md +137 -0
- deviate/prompts/auto/judge.md +260 -0
- deviate/prompts/auto/plan.md +127 -0
- deviate/prompts/auto/prd.md +108 -0
- deviate/prompts/auto/red.md +156 -0
- deviate/prompts/auto/refactor.md +166 -0
- deviate/prompts/auto/research.md +111 -0
- deviate/prompts/auto/shard.md +90 -0
- deviate/prompts/auto/specify.md +77 -0
- deviate/prompts/auto/tasks.md +191 -0
- deviate/prompts/commands/deviate-adhoc.md +216 -0
- deviate/prompts/commands/deviate-architecture.md +147 -0
- deviate/prompts/commands/deviate-constitution.md +215 -0
- deviate/prompts/commands/deviate-e2e.md +264 -0
- deviate/prompts/commands/deviate-execute.md +223 -0
- deviate/prompts/commands/deviate-explore.md +253 -0
- deviate/prompts/commands/deviate-flows.md +226 -0
- deviate/prompts/commands/deviate-green.md +217 -0
- deviate/prompts/commands/deviate-hotfix.md +188 -0
- deviate/prompts/commands/deviate-init.md +170 -0
- deviate/prompts/commands/deviate-judge.md +193 -0
- deviate/prompts/commands/deviate-merge.md +221 -0
- deviate/prompts/commands/deviate-plan.md +158 -0
- deviate/prompts/commands/deviate-pr.md +144 -0
- deviate/prompts/commands/deviate-prd.md +216 -0
- deviate/prompts/commands/deviate-prune.md +260 -0
- deviate/prompts/commands/deviate-red.md +231 -0
- deviate/prompts/commands/deviate-refactor.md +211 -0
- deviate/prompts/commands/deviate-release.md +123 -0
- deviate/prompts/commands/deviate-research.md +359 -0
- deviate/prompts/commands/deviate-review.md +289 -0
- deviate/prompts/commands/deviate-shard.md +226 -0
- deviate/prompts/commands/deviate-tasks.md +281 -0
- deviate/prompts/commands/deviate-triage.md +141 -0
- deviate/prompts/constitution_seed.md +51 -0
- deviate/prompts/core/core.md +21 -0
- deviate/prompts/core/macro-auto.md +59 -0
- deviate/prompts/core/macro-command.md +54 -0
- deviate/prompts/core/macro-skill.md +54 -0
- deviate/prompts/core/meso-auto.md +63 -0
- deviate/prompts/core/meso-command.md +58 -0
- deviate/prompts/core/meso-skill.md +58 -0
- deviate/prompts/core/micro-auto.md +76 -0
- deviate/prompts/core/micro-command.md +67 -0
- deviate/prompts/core/micro-skill.md +67 -0
- deviate/prompts/extras/deviate-pr-graphite-routing.md +20 -0
- deviate/prompts/governance/__init__.py +0 -0
- deviate/prompts/governance/agents_seed.md +1 -0
- deviate/prompts/governance/claudemd_seed.md +1 -0
- deviate/prompts/governance/graphite_seed.md +18 -0
- deviate/prompts/governance/libref_seed.md +3 -0
- deviate/state/__init__.py +0 -0
- deviate/state/config.py +257 -0
- deviate/state/ledger.py +407 -0
- deviate/ui/__init__.py +5 -0
- deviate/ui/monitor.py +187 -0
- deviate/ui/render.py +26 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<micro_layer_model>
|
|
2
|
+
|
|
3
|
+
NOTE: This differs from ``micro-skill.md`` — the ``-auto`` variants reference the CLI orchestrator,
|
|
4
|
+
while ``-skill`` variants reference the pre/post scripts directly, because skill prompts are invoked
|
|
5
|
+
by the agent directly (agent runs pre/post) while auto prompts are orchestrated by the CLI.
|
|
6
|
+
|
|
7
|
+
This phase operates inside the **MICRO LAYER** — the Red-Green-Refactor cycle for individual tasks.
|
|
8
|
+
|
|
9
|
+
<rgr_cycle>
|
|
10
|
+
|
|
11
|
+
Each task is a Logical Unit (30-90 min) that undergoes ONE complete R-G-R cycle:
|
|
12
|
+
|
|
13
|
+
<item>
|
|
14
|
+
**RED**: Write a failing test — verified to fail due to missing implementation, not syntax errors.
|
|
15
|
+
</item>
|
|
16
|
+
|
|
17
|
+
<item>
|
|
18
|
+
**GREEN**: Write the minimum production code to pass the test.
|
|
19
|
+
</item>
|
|
20
|
+
|
|
21
|
+
<item>
|
|
22
|
+
**REFACTOR**: Behavior-preserving structural cleanup without modifying tests.
|
|
23
|
+
</item>
|
|
24
|
+
|
|
25
|
+
</rgr_cycle>
|
|
26
|
+
|
|
27
|
+
<shared_disciplines>
|
|
28
|
+
|
|
29
|
+
<item>
|
|
30
|
+
<title>Test-First Discipline</title>
|
|
31
|
+
No production code is written before a failing test exists. Tests are the executable specification — the RED phase verifies the test fails before GREEN begins.
|
|
32
|
+
</item>
|
|
33
|
+
|
|
34
|
+
<item>
|
|
35
|
+
<title>Sociable Tests Over Solitary</title>
|
|
36
|
+
Prefer sociable (integration) tests that exercise real component orchestration. Restrict mocking exclusively to non-deterministic external networks, third-party transactional interfaces, or volatile system attributes (system epoch timers, cryptographic entropy paths).
|
|
37
|
+
</item>
|
|
38
|
+
|
|
39
|
+
<item>
|
|
40
|
+
<title>Verification-is-Done</title>
|
|
41
|
+
A task is ONLY finished when its `Verification` command passes. Verification is deterministic and scoped — run the specific test file, not the entire suite.
|
|
42
|
+
</item>
|
|
43
|
+
|
|
44
|
+
<item>
|
|
45
|
+
<title>Git Isolation</title>
|
|
46
|
+
Any test that invokes git operations MUST operate on an isolated temporary directory initialized as a fresh git repo. Tests MUST NOT run git commands within the real project repository. Use `create_temp_dir` → `git init` → copy fixtures → run test in that context.
|
|
47
|
+
</item>
|
|
48
|
+
|
|
49
|
+
<item>
|
|
50
|
+
<title>Orchestrator Lifecycle</title>
|
|
51
|
+
The CLI orchestrator handles all pre/post lifecycle — it injects context, stages files, runs pre-commit hooks, updates the task ledger, and commits. Do NOT run `deviate <phase> pre/post` or use `git add`/`git commit` directly.
|
|
52
|
+
</item>
|
|
53
|
+
|
|
54
|
+
<item>
|
|
55
|
+
<title>YAML Quoting Rule</title>
|
|
56
|
+
ALL string values in the handover manifest YAML MUST be wrapped in double quotes. A value containing a colon (`:`) will BREAK YAML parsing if unquoted.
|
|
57
|
+
</item>
|
|
58
|
+
|
|
59
|
+
<item>
|
|
60
|
+
<title>Flow-Anchored Implementation</title>
|
|
61
|
+
Micro phases are where Product-layer context is at the highest risk of being lost. Every micro phase MUST (a) read the active task's `**Flow References**` field from `tasks.md` before writing any code, (b) restate the user-visible flow(s) the task serves in the handover manifest under `flow_refs`, and (c) verify the test or implementation exercises behavior derivable from those flows — not implementation details detached from user intent. **judge** MUST add a `flow_alignment` rubric dimension alongside Spec Compliance: does the diff preserve or extend the flows named in the task's `**Flow References**`? A change that silently abandons or breaks a named flow MUST fail JUDGE with severity HIGH and a `train_feedback` block instructing the next GREEN attempt to re-anchor to the flow. **red** MUST write tests that describe user-visible behavior derivable from the parent flow's Trigger and Happy Path, not internal function signatures. **green** MUST implement the minimum production code to satisfy those flow-anchored tests, restricting scope to workstation files explicitly tied to the named flow. **refactor**, **yellow**, and **execute** inherit the same flow context and MUST NOT extend scope beyond what the named flow requires. If `tasks.md` carries `**Flow References**: []`, treat the task as enabling/infrastructure (no flow anchor required) but still surface the empty list in the handover manifest.
|
|
62
|
+
</item>
|
|
63
|
+
|
|
64
|
+
</shared_disciplines>
|
|
65
|
+
|
|
66
|
+
</micro_layer_model>
|
|
67
|
+
|
|
68
|
+
<mandate>
|
|
69
|
+
STDOUT OUTPUT MANDATE: Your final stdout response must be EXACTLY the YAML block from the `<handover_manifest>` section. No conversational text, no analysis, no commentary, no markdown formatting, no file content on stdout. Write artifact files to their target paths only (not to stdout). The caller parses your stdout as raw YAML.
|
|
70
|
+
</mandate>
|
|
71
|
+
|
|
72
|
+
<context>
|
|
73
|
+
<user_input>
|
|
74
|
+
$ARGUMENTS
|
|
75
|
+
</user_input>
|
|
76
|
+
</context>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<micro_layer_model>
|
|
2
|
+
|
|
3
|
+
This phase operates inside the **MICRO LAYER** — the Red-Green-Refactor cycle for individual tasks.
|
|
4
|
+
|
|
5
|
+
<rgr_cycle>
|
|
6
|
+
|
|
7
|
+
Each task is a Logical Unit (30-90 min) that undergoes ONE complete R-G-R cycle:
|
|
8
|
+
|
|
9
|
+
<item>
|
|
10
|
+
**RED**: Write a failing test — verified to fail due to missing implementation, not syntax errors.
|
|
11
|
+
</item>
|
|
12
|
+
|
|
13
|
+
<item>
|
|
14
|
+
**GREEN**: Write the minimum production code to pass the test.
|
|
15
|
+
</item>
|
|
16
|
+
|
|
17
|
+
<item>
|
|
18
|
+
**REFACTOR**: Behavior-preserving structural cleanup without modifying tests.
|
|
19
|
+
</item>
|
|
20
|
+
|
|
21
|
+
</rgr_cycle>
|
|
22
|
+
|
|
23
|
+
<shared_disciplines>
|
|
24
|
+
|
|
25
|
+
<item>
|
|
26
|
+
<title>Test-First Discipline</title>
|
|
27
|
+
No production code is written before a failing test exists. Tests are the executable specification — the RED phase verifies the test fails before GREEN begins.
|
|
28
|
+
</item>
|
|
29
|
+
|
|
30
|
+
<item>
|
|
31
|
+
<title>Sociable Tests Over Solitary</title>
|
|
32
|
+
Prefer sociable (integration) tests that exercise real component orchestration. Restrict mocking exclusively to non-deterministic external networks, third-party transactional interfaces, or volatile system attributes (system epoch timers, cryptographic entropy paths).
|
|
33
|
+
</item>
|
|
34
|
+
|
|
35
|
+
<item>
|
|
36
|
+
<title>Verification-is-Done</title>
|
|
37
|
+
A task is ONLY finished when its `Verification` command passes and the post-script commits successfully. Verification is deterministic and scoped — run the specific test file, not the entire suite.
|
|
38
|
+
</item>
|
|
39
|
+
|
|
40
|
+
<item>
|
|
41
|
+
<title>Git Isolation</title>
|
|
42
|
+
Any test that invokes git operations MUST operate on an isolated temporary directory initialized as a fresh git repo. Tests MUST NOT run git commands within the real project repository. Use `create_temp_dir` → `git init` → copy fixtures → run test in that context.
|
|
43
|
+
</item>
|
|
44
|
+
|
|
45
|
+
<item>
|
|
46
|
+
<title>Post-Script Protocol</title>
|
|
47
|
+
Every micro phase ends with `deviate <phase> post`. This is MANDATORY — do NOT use `git add` / `git commit` directly. The post-script stages files, runs pre-commit hooks (lint, format-check, tests), updates the task ledger, and commits. Allocate a timeout of at least 180s (3 minutes) for post-script execution.
|
|
48
|
+
</item>
|
|
49
|
+
|
|
50
|
+
<item>
|
|
51
|
+
<title>Handover Manifest YAML</title>
|
|
52
|
+
After post-script success, emit a handover manifest as a YAML code block. ALL string values MUST be wrapped in double quotes. A value containing a colon (`:`) will BREAK YAML parsing if unquoted. Output NOTHING outside the YAML block — no explanations, no commentary.
|
|
53
|
+
</item>
|
|
54
|
+
|
|
55
|
+
<item>
|
|
56
|
+
<title>Offline Documentation Guidance</title>
|
|
57
|
+
When implementing, use `libref query <library> <topic>` to look up library APIs and framework conventions. The `libref` CLI provides offline, version-pinned documentation — prefer it over web fetching. If `libref` is unavailable, fall back to training data or web fetch.
|
|
58
|
+
</item>
|
|
59
|
+
|
|
60
|
+
<item>
|
|
61
|
+
<title>Flow-Anchored Implementation</title>
|
|
62
|
+
Micro phases are where Product-layer context is at the highest risk of being lost. Every micro phase MUST (a) read the active task's `**Flow References**` field from `tasks.md` before writing any code, (b) restate the user-visible flow(s) the task serves in the handover manifest under `flow_refs`, and (c) verify the test or implementation exercises behavior derivable from those flows — not implementation details detached from user intent. **judge** MUST add a `flow_alignment` rubric dimension alongside Spec Compliance: does the diff preserve or extend the flows named in the task's `**Flow References**`? A change that silently abandons or breaks a named flow MUST fail JUDGE with severity HIGH and a `train_feedback` block instructing the next GREEN attempt to re-anchor to the flow. **red** MUST write tests that describe user-visible behavior derivable from the parent flow's Trigger and Happy Path, not internal function signatures. **green** MUST implement the minimum production code to satisfy those flow-anchored tests, restricting scope to workstation files explicitly tied to the named flow. **refactor**, **yellow**, and **execute** inherit the same flow context and MUST NOT extend scope beyond what the named flow requires. If `tasks.md` carries `**Flow References**: []`, treat the task as enabling/infrastructure (no flow anchor required) but still surface the empty list in the handover manifest.
|
|
63
|
+
</item>
|
|
64
|
+
|
|
65
|
+
</shared_disciplines>
|
|
66
|
+
|
|
67
|
+
</micro_layer_model>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<micro_layer_model>
|
|
2
|
+
|
|
3
|
+
This phase operates inside the **MICRO LAYER** — the Red-Green-Refactor cycle for individual tasks.
|
|
4
|
+
|
|
5
|
+
<rgr_cycle>
|
|
6
|
+
|
|
7
|
+
Each task is a Logical Unit (30-90 min) that undergoes ONE complete R-G-R cycle:
|
|
8
|
+
|
|
9
|
+
<item>
|
|
10
|
+
**RED**: Write a failing test — verified to fail due to missing implementation, not syntax errors.
|
|
11
|
+
</item>
|
|
12
|
+
|
|
13
|
+
<item>
|
|
14
|
+
**GREEN**: Write the minimum production code to pass the test.
|
|
15
|
+
</item>
|
|
16
|
+
|
|
17
|
+
<item>
|
|
18
|
+
**REFACTOR**: Behavior-preserving structural cleanup without modifying tests.
|
|
19
|
+
</item>
|
|
20
|
+
|
|
21
|
+
</rgr_cycle>
|
|
22
|
+
|
|
23
|
+
<shared_disciplines>
|
|
24
|
+
|
|
25
|
+
<item>
|
|
26
|
+
<title>Test-First Discipline</title>
|
|
27
|
+
No production code is written before a failing test exists. Tests are the executable specification — the RED phase verifies the test fails before GREEN begins.
|
|
28
|
+
</item>
|
|
29
|
+
|
|
30
|
+
<item>
|
|
31
|
+
<title>Sociable Tests Over Solitary</title>
|
|
32
|
+
Prefer sociable (integration) tests that exercise real component orchestration. Restrict mocking exclusively to non-deterministic external networks, third-party transactional interfaces, or volatile system attributes (system epoch timers, cryptographic entropy paths).
|
|
33
|
+
</item>
|
|
34
|
+
|
|
35
|
+
<item>
|
|
36
|
+
<title>Verification-is-Done</title>
|
|
37
|
+
A task is ONLY finished when its `Verification` command passes and the post-script commits successfully. Verification is deterministic and scoped — run the specific test file, not the entire suite.
|
|
38
|
+
</item>
|
|
39
|
+
|
|
40
|
+
<item>
|
|
41
|
+
<title>Git Isolation</title>
|
|
42
|
+
Any test that invokes git operations MUST operate on an isolated temporary directory initialized as a fresh git repo. Tests MUST NOT run git commands within the real project repository. Use `create_temp_dir` → `git init` → copy fixtures → run test in that context.
|
|
43
|
+
</item>
|
|
44
|
+
|
|
45
|
+
<item>
|
|
46
|
+
<title>Post-Script Protocol</title>
|
|
47
|
+
Every micro phase ends with `deviate <phase> post`. This is MANDATORY — do NOT use `git add` / `git commit` directly. The post-script stages files, runs pre-commit hooks (lint, format-check, tests), updates the task ledger, and commits. Allocate a timeout of at least 180s (3 minutes) for post-script execution.
|
|
48
|
+
</item>
|
|
49
|
+
|
|
50
|
+
<item>
|
|
51
|
+
<title>Handover Manifest YAML</title>
|
|
52
|
+
After post-script success, emit a handover manifest as a YAML code block. ALL string values MUST be wrapped in double quotes. A value containing a colon (`:`) will BREAK YAML parsing if unquoted. Output NOTHING outside the YAML block — no explanations, no commentary.
|
|
53
|
+
</item>
|
|
54
|
+
|
|
55
|
+
<item>
|
|
56
|
+
<title>Offline Documentation Guidance</title>
|
|
57
|
+
When implementing, use `libref query <library> <topic>` to look up library APIs and framework conventions. The `libref` CLI provides offline, version-pinned documentation — prefer it over web fetching. If `libref` is unavailable, fall back to training data or web fetch.
|
|
58
|
+
</item>
|
|
59
|
+
|
|
60
|
+
<item>
|
|
61
|
+
<title>Flow-Anchored Implementation</title>
|
|
62
|
+
Micro phases are where Product-layer context is at the highest risk of being lost. Every micro phase MUST (a) read the active task's `**Flow References**` field from `tasks.md` before writing any code, (b) restate the user-visible flow(s) the task serves in the handover manifest under `flow_refs`, and (c) verify the test or implementation exercises behavior derivable from those flows — not implementation details detached from user intent. **judge** MUST add a `flow_alignment` rubric dimension alongside Spec Compliance: does the diff preserve or extend the flows named in the task's `**Flow References**`? A change that silently abandons or breaks a named flow MUST fail JUDGE with severity HIGH and a `train_feedback` block instructing the next GREEN attempt to re-anchor to the flow. **red** MUST write tests that describe user-visible behavior derivable from the parent flow's Trigger and Happy Path, not internal function signatures. **green** MUST implement the minimum production code to satisfy those flow-anchored tests, restricting scope to workstation files explicitly tied to the named flow. **refactor**, **yellow**, and **execute** inherit the same flow context and MUST NOT extend scope beyond what the named flow requires. If `tasks.md` carries `**Flow References**: []`, treat the task as enabling/infrastructure (no flow anchor required) but still surface the empty list in the handover manifest.
|
|
63
|
+
</item>
|
|
64
|
+
|
|
65
|
+
</shared_disciplines>
|
|
66
|
+
|
|
67
|
+
</micro_layer_model>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<graphite_routing>
|
|
2
|
+
When Graphite is enabled (`.deviate/config.toml` has `graphite = true`), this skill routes PR operations through the Graphite CLI (`gt`) instead of the GitHub CLI (`gh`).
|
|
3
|
+
|
|
4
|
+
<pr_submission>
|
|
5
|
+
- Use `gt submit --stack --no-edit` to submit the entire stacked branch for review.
|
|
6
|
+
- After `gt submit --stack`, the `deviate pr run` command calls `_update_gt_prs()` in `src/deviate/cli/meso.py:959` which parses the submit output for PR URLs and runs `gh pr edit <number> --title <title> --body-file <path>` on each created PR. This is because `gt submit` has no `--title` or `--body-file` flag — `gh pr edit` post-submit is the only non-interactive fixup path.
|
|
7
|
+
- Do NOT invoke `gh pr create` directly — Graphite owns PR creation from stacked branches.
|
|
8
|
+
- The `--merge` and `--auto-merge` flags are incompatible with `gt submit --stack` and will be ignored at runtime; surface a warning if they were requested.
|
|
9
|
+
</pr_submission>
|
|
10
|
+
|
|
11
|
+
<branch_creation_between_tasks>
|
|
12
|
+
When `graphite = true` and the micro layer runs all tasks via `_run_all()` in `src/deviate/cli/micro.py:1854`, it calls `gt create -m "feat(<next_id>): <description>"` after each successful task (except the last) to create a stacked branch for the next task. This ensures each task lands on its own branch in the Graphite stack.
|
|
13
|
+
</branch_creation_between_tasks>
|
|
14
|
+
|
|
15
|
+
<anti_patterns>
|
|
16
|
+
- Do NOT call `git checkout -b` to create the issue branch — `gt create -am` was already used by the meso layer.
|
|
17
|
+
- Do NOT mix `gh` and `gt` commands in the same PR lifecycle — pick one based on the config flag.
|
|
18
|
+
- Do NOT skip the Graphite routing even if the user requests `gh pr create` manually — defer to the runtime config.
|
|
19
|
+
</anti_patterns>
|
|
20
|
+
</graphite_routing>
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!-- Intentionally empty: the DeviaTDD Phase Architecture block was project-internal guidance that did not help consuming projects. Removed 2026-06-28. -->
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!-- Intentionally empty: the DeviaTDD Phase Architecture block was project-internal guidance that did not help consuming projects. Removed 2026-06-28. -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Graphite Stacked Changes Workflow
|
|
2
|
+
|
|
3
|
+
When Graphite integration is enabled (`.deviate/config.toml` contains `graphite = true`):
|
|
4
|
+
|
|
5
|
+
### Branch Creation
|
|
6
|
+
- Use `gt create -am "<message>"` to create a branch with an automatic commit
|
|
7
|
+
- If the working tree is clean, use `gt create -m "<message>"` instead
|
|
8
|
+
|
|
9
|
+
### PR Submission
|
|
10
|
+
- Use `gt submit --stack` to submit the entire stack for review
|
|
11
|
+
|
|
12
|
+
### Syncing
|
|
13
|
+
- Use `gt sync` to sync your stack with the remote
|
|
14
|
+
|
|
15
|
+
### Anti-Patterns
|
|
16
|
+
- Do NOT use `git checkout -b` alongside `gt` — it bypasses Graphite's stack tracking
|
|
17
|
+
- Do NOT use `gh pr create` when Graphite is enabled — use `gt submit --stack`
|
|
18
|
+
- Do NOT run `gt` commands outside a Graphite-tracked repository
|
|
File without changes
|
deviate/state/config.py
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import tomllib
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Literal, Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field, field_validator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentConfig(BaseModel):
|
|
13
|
+
# Agent backend: "opencode", "claude", "droid", "pi", or "omp"
|
|
14
|
+
backend: Literal["opencode", "claude", "droid", "pi", "omp"] = "opencode"
|
|
15
|
+
# Agent invocation timeout in seconds (must be > 0)
|
|
16
|
+
timeout: int = Field(default=600, gt=0)
|
|
17
|
+
# Opt-in RPC mode for Pi — spawns `pi --mode rpc --no-session` instead of `pi -p`
|
|
18
|
+
pi_rpc: bool = Field(
|
|
19
|
+
default=False,
|
|
20
|
+
description="Opt-in RPC mode for Pi (spawns pi --mode rpc --no-session instead of pi -p)",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
model_config = {"extra": "forbid"}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_VALID_PHASES = frozenset(
|
|
27
|
+
{
|
|
28
|
+
"IDLE",
|
|
29
|
+
"EXPLORE",
|
|
30
|
+
"RESEARCH",
|
|
31
|
+
"PRD",
|
|
32
|
+
"SHARD",
|
|
33
|
+
"SPECIFY",
|
|
34
|
+
"PLAN",
|
|
35
|
+
"TASKS",
|
|
36
|
+
"RED",
|
|
37
|
+
"GREEN",
|
|
38
|
+
"JUDGE",
|
|
39
|
+
"REFACTOR",
|
|
40
|
+
"E2E",
|
|
41
|
+
"EXECUTE",
|
|
42
|
+
"HOTFIX",
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
_PHASE_ARTIFACT_MAP: dict[str, tuple[str, ...]] = {
|
|
47
|
+
"RESEARCH": ("explore.md",),
|
|
48
|
+
"PRD": ("design.md", "data-model.md"),
|
|
49
|
+
"SHARD": ("prd.md",),
|
|
50
|
+
"SPECIFY": ("spec.md",),
|
|
51
|
+
"PLAN": ("plan.md",),
|
|
52
|
+
"TASKS": ("spec.md", "tasks.md"),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TransitionViolationError(Exception):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Module-level utility functions (extracted from SessionState static methods)
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_filesystem_state(
|
|
66
|
+
phase: str,
|
|
67
|
+
epic_slug: str | None,
|
|
68
|
+
repo_path: Path,
|
|
69
|
+
) -> list[str]:
|
|
70
|
+
expected_artifacts = _PHASE_ARTIFACT_MAP.get(phase, ())
|
|
71
|
+
missing: list[str] = []
|
|
72
|
+
for artifact in expected_artifacts:
|
|
73
|
+
artifact_path = (
|
|
74
|
+
repo_path / "specs" / epic_slug / artifact
|
|
75
|
+
if epic_slug
|
|
76
|
+
else repo_path / artifact
|
|
77
|
+
)
|
|
78
|
+
if not artifact_path.exists():
|
|
79
|
+
missing.append(artifact)
|
|
80
|
+
return missing
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def reconstruct_from_worktree(worktree: Path) -> SessionState:
|
|
84
|
+
has_spec = (worktree / "spec.md").exists()
|
|
85
|
+
has_plan = (worktree / "plan.md").exists()
|
|
86
|
+
has_tasks = (worktree / "tasks.md").exists()
|
|
87
|
+
if has_plan and has_tasks:
|
|
88
|
+
phase = "TASKS"
|
|
89
|
+
elif has_plan:
|
|
90
|
+
phase = "PLAN"
|
|
91
|
+
elif has_spec and has_tasks:
|
|
92
|
+
phase = "TASKS"
|
|
93
|
+
elif has_spec:
|
|
94
|
+
phase = "SPECIFY"
|
|
95
|
+
else:
|
|
96
|
+
phase = "IDLE"
|
|
97
|
+
return SessionState(current_phase=phase)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def normalize_task_id(ref: str) -> str:
|
|
101
|
+
return ref.rstrip(":")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class DeviateConfig(BaseModel):
|
|
105
|
+
# Profile name — defines preset config groups (default, full, fast, secure)
|
|
106
|
+
profile: str = "default"
|
|
107
|
+
# CLI inactivity timeout in seconds (must be > 0)
|
|
108
|
+
timeout_seconds: int = Field(default=300, gt=0)
|
|
109
|
+
# Agent export mode: "local" (project .claude/) or "global" (~/.claude/)
|
|
110
|
+
agent_export_mode: Literal["local", "global"] = "local"
|
|
111
|
+
# Agent backend config (opencode, claude, or droid)
|
|
112
|
+
agent: AgentConfig = Field(default_factory=AgentConfig)
|
|
113
|
+
# Per-phase model overrides, e.g. default = "opencode/deepseek-v4-flash"
|
|
114
|
+
models: dict[str, str] = Field(default_factory=dict)
|
|
115
|
+
# Enable the libref CLI for offline documentation lookups
|
|
116
|
+
use_libref: bool = False
|
|
117
|
+
# Enable Graphite CLI integration for stacked changes
|
|
118
|
+
graphite: bool = Field(default=False)
|
|
119
|
+
|
|
120
|
+
model_config = {"extra": "forbid"}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _load_deviate_config_toml(root: Path) -> dict | None:
|
|
124
|
+
"""Load the `.deviate/config.toml` file, returning a dict or None."""
|
|
125
|
+
config_path = root / ".deviate" / "config.toml"
|
|
126
|
+
if not config_path.exists():
|
|
127
|
+
return None
|
|
128
|
+
try:
|
|
129
|
+
with open(config_path, "rb") as f:
|
|
130
|
+
return tomllib.load(f)
|
|
131
|
+
except Exception:
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def resolve_phase_model(phase: str, models: dict[str, str]) -> str | None:
|
|
136
|
+
"""Resolve the model ID for *phase* from a `[models]` dict.
|
|
137
|
+
|
|
138
|
+
Resolution order (case-insensitive):
|
|
139
|
+
1. Phase-specific key (e.g. ``judge``, ``plan``, ``red``)
|
|
140
|
+
2. ``default`` key
|
|
141
|
+
3. ``None`` — backend falls back to its native default
|
|
142
|
+
"""
|
|
143
|
+
if not models:
|
|
144
|
+
return None
|
|
145
|
+
phase_lower = phase.lower()
|
|
146
|
+
lookup = {k.lower(): val for k, val in models.items() if val}
|
|
147
|
+
if phase_lower in lookup:
|
|
148
|
+
return lookup[phase_lower]
|
|
149
|
+
if "default" in lookup:
|
|
150
|
+
return lookup["default"]
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def resolve_model_for_phase(phase: str, root: Path) -> str | None:
|
|
155
|
+
"""Load `[models]` from `.deviate/config.toml` and resolve *phase*.
|
|
156
|
+
|
|
157
|
+
Backed by :func:`resolve_phase_model`. ``opencode`` and ``droid``
|
|
158
|
+
backends accept ``--model <id>``; the ``claude`` backend ignores the
|
|
159
|
+
resolved value silently.
|
|
160
|
+
"""
|
|
161
|
+
data = _load_deviate_config_toml(root)
|
|
162
|
+
if data is None:
|
|
163
|
+
return None
|
|
164
|
+
models = data.get("models", {})
|
|
165
|
+
if not isinstance(models, dict):
|
|
166
|
+
return None
|
|
167
|
+
return resolve_phase_model(phase, {k: str(v) for k, v in models.items()})
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def resolve_graphite_config(root: Path) -> bool:
|
|
171
|
+
"""Check whether the Graphite integration is enabled in `.deviate/config.toml`."""
|
|
172
|
+
data = _load_deviate_config_toml(root)
|
|
173
|
+
if data is None:
|
|
174
|
+
return False
|
|
175
|
+
value = data.get("graphite", False)
|
|
176
|
+
return value if isinstance(value, bool) else False
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class PytestReportConfig(BaseModel):
|
|
180
|
+
json_report: bool = False
|
|
181
|
+
|
|
182
|
+
model_config = {"extra": "forbid"}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class ProfileConfig(BaseModel):
|
|
186
|
+
default: Literal["full", "fast", "secure"] = "full"
|
|
187
|
+
|
|
188
|
+
model_config = {"extra": "forbid"}
|
|
189
|
+
|
|
190
|
+
def to_toml_string(self) -> str:
|
|
191
|
+
return 'default = "{}"\n'.format(self.default)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class SessionState(BaseModel):
|
|
195
|
+
current_phase: str = "IDLE"
|
|
196
|
+
active_issue_id: Optional[str] = None
|
|
197
|
+
last_command: str = ""
|
|
198
|
+
train_feedback: str = ""
|
|
199
|
+
judge_rejected: bool = False
|
|
200
|
+
red_commit_sha: str = ""
|
|
201
|
+
timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
|
|
202
|
+
|
|
203
|
+
@field_validator("current_phase")
|
|
204
|
+
@classmethod
|
|
205
|
+
def _validate_phase(cls, v: str) -> str:
|
|
206
|
+
if v not in _VALID_PHASES:
|
|
207
|
+
valid = ", ".join(sorted(_VALID_PHASES))
|
|
208
|
+
raise ValueError(f"Invalid phase '{v}'. Must be one of: {valid}")
|
|
209
|
+
return v
|
|
210
|
+
|
|
211
|
+
def transition_to(self, phase: str) -> SessionState:
|
|
212
|
+
return SessionState(
|
|
213
|
+
current_phase=phase,
|
|
214
|
+
active_issue_id=self.active_issue_id,
|
|
215
|
+
last_command=self.last_command,
|
|
216
|
+
red_commit_sha=self.red_commit_sha,
|
|
217
|
+
timestamp=datetime.now(timezone.utc),
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
def force_transition_to(self, phase: str) -> SessionState:
|
|
221
|
+
return SessionState(
|
|
222
|
+
current_phase=phase,
|
|
223
|
+
active_issue_id=self.active_issue_id,
|
|
224
|
+
last_command=self.last_command,
|
|
225
|
+
red_commit_sha=self.red_commit_sha,
|
|
226
|
+
train_feedback=self.train_feedback,
|
|
227
|
+
judge_rejected=self.judge_rejected,
|
|
228
|
+
timestamp=datetime.now(timezone.utc),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
def save(self, path: Path) -> None:
|
|
232
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
233
|
+
path.write_text(self.model_dump_json(indent=2), encoding="utf-8")
|
|
234
|
+
|
|
235
|
+
@classmethod
|
|
236
|
+
def load(cls, path: Path) -> SessionState:
|
|
237
|
+
if not path.exists():
|
|
238
|
+
return cls()
|
|
239
|
+
raw = path.read_text(encoding="utf-8")
|
|
240
|
+
data = json.loads(raw)
|
|
241
|
+
return cls.model_validate(data)
|
|
242
|
+
|
|
243
|
+
@staticmethod
|
|
244
|
+
def validate_filesystem_state(
|
|
245
|
+
phase: str,
|
|
246
|
+
epic_slug: str | None,
|
|
247
|
+
repo_path: Path,
|
|
248
|
+
) -> list[str]:
|
|
249
|
+
return validate_filesystem_state(phase, epic_slug, repo_path)
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def reconstruct_from_worktree(worktree: Path) -> SessionState:
|
|
253
|
+
return reconstruct_from_worktree(worktree)
|
|
254
|
+
|
|
255
|
+
@staticmethod
|
|
256
|
+
def normalize_task_id(ref: str) -> str:
|
|
257
|
+
return normalize_task_id(ref)
|