duaer-spec 0.1.0

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.
Files changed (75) hide show
  1. package/.cursor/rules/agents-workflow.mdc +50 -0
  2. package/.cursor/rules/ai-ui-copy.mdc +12 -0
  3. package/.cursor/rules/duaer-spec.mdc +33 -0
  4. package/.cursor/skills/duaer-analyze/SKILL.md +259 -0
  5. package/.cursor/skills/duaer-checklist/SKILL.md +383 -0
  6. package/.cursor/skills/duaer-clarify/SKILL.md +291 -0
  7. package/.cursor/skills/duaer-constitution/SKILL.md +177 -0
  8. package/.cursor/skills/duaer-converge/SKILL.md +277 -0
  9. package/.cursor/skills/duaer-git-commit/SKILL.md +68 -0
  10. package/.cursor/skills/duaer-git-feature/SKILL.md +94 -0
  11. package/.cursor/skills/duaer-git-initialize/SKILL.md +54 -0
  12. package/.cursor/skills/duaer-git-remote/SKILL.md +50 -0
  13. package/.cursor/skills/duaer-git-validate/SKILL.md +54 -0
  14. package/.cursor/skills/duaer-implement/SKILL.md +226 -0
  15. package/.cursor/skills/duaer-plan/SKILL.md +166 -0
  16. package/.cursor/skills/duaer-specify/SKILL.md +345 -0
  17. package/.cursor/skills/duaer-tasks/SKILL.md +214 -0
  18. package/.cursor/skills/duaer-taskstoissues/SKILL.md +109 -0
  19. package/.duaer/extensions/.registry +23 -0
  20. package/.duaer/extensions/git/README.md +119 -0
  21. package/.duaer/extensions/git/commands/duaer.git.commit.md +63 -0
  22. package/.duaer/extensions/git/commands/duaer.git.feature.md +82 -0
  23. package/.duaer/extensions/git/commands/duaer.git.initialize.md +49 -0
  24. package/.duaer/extensions/git/commands/duaer.git.remote.md +45 -0
  25. package/.duaer/extensions/git/commands/duaer.git.validate.md +49 -0
  26. package/.duaer/extensions/git/config-template.yml +79 -0
  27. package/.duaer/extensions/git/extension.yml +142 -0
  28. package/.duaer/extensions/git/git-config.yml +79 -0
  29. package/.duaer/extensions/git/scripts/bash/auto-commit.sh +211 -0
  30. package/.duaer/extensions/git/scripts/bash/create-new-feature-branch.sh +626 -0
  31. package/.duaer/extensions/git/scripts/bash/git-common.sh +56 -0
  32. package/.duaer/extensions/git/scripts/bash/initialize-repo.sh +54 -0
  33. package/.duaer/extensions/git/scripts/powershell/auto-commit.ps1 +230 -0
  34. package/.duaer/extensions/git/scripts/powershell/create-new-feature-branch.ps1 +592 -0
  35. package/.duaer/extensions/git/scripts/powershell/git-common.ps1 +52 -0
  36. package/.duaer/extensions/git/scripts/powershell/initialize-repo.ps1 +69 -0
  37. package/.duaer/extensions/git/scripts/python/auto_commit.py +195 -0
  38. package/.duaer/extensions/git/scripts/python/create_new_feature_branch.py +634 -0
  39. package/.duaer/extensions/git/scripts/python/git_common.py +81 -0
  40. package/.duaer/extensions/git/scripts/python/initialize_repo.py +89 -0
  41. package/.duaer/extensions.yml +167 -0
  42. package/.duaer/init-options.json +9 -0
  43. package/.duaer/integration.json +15 -0
  44. package/.duaer/integrations/cursor-agent.manifest.json +17 -0
  45. package/.duaer/integrations/duaer.manifest.json +19 -0
  46. package/.duaer/memory/.constitution-template.json +4 -0
  47. package/.duaer/memory/constitution.md +37 -0
  48. package/.duaer/memory/project-context.md +24 -0
  49. package/.duaer/memory/testing.md +14 -0
  50. package/.duaer/scripts/bash/check-prerequisites.sh +243 -0
  51. package/.duaer/scripts/bash/common.sh +926 -0
  52. package/.duaer/scripts/bash/create-new-feature.sh +407 -0
  53. package/.duaer/scripts/bash/resolve-template.sh +57 -0
  54. package/.duaer/scripts/bash/setup-plan.sh +85 -0
  55. package/.duaer/scripts/bash/setup-tasks.sh +94 -0
  56. package/.duaer/templates/checklist-template.md +45 -0
  57. package/.duaer/templates/constitution-template.md +50 -0
  58. package/.duaer/templates/plan-template.md +113 -0
  59. package/.duaer/templates/spec-template.md +131 -0
  60. package/.duaer/templates/tasks-template.md +252 -0
  61. package/.duaer/workflows/duaer/workflow.yml +78 -0
  62. package/.duaer/workflows/workflow-registry.json +13 -0
  63. package/ADOPT.md +75 -0
  64. package/AGENTS.md +290 -0
  65. package/CHANGELOG.md +28 -0
  66. package/DUADER.md +57 -0
  67. package/LICENSE +21 -0
  68. package/README.md +74 -0
  69. package/bin/duaer.mjs +298 -0
  70. package/docs/agent/README.md +12 -0
  71. package/docs/agent/change-checklist.md +130 -0
  72. package/docs/agent/e2e-test-plan.md +33 -0
  73. package/docs/agent/workflow.md +127 -0
  74. package/docs/baseline.md +10 -0
  75. package/package.json +49 -0
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env python3
2
+ """Git-specific common helpers for the git extension.
3
+
4
+ Python port of ``git-common.sh`` / ``git-common.ps1`` — contains only
5
+ git-specific branch validation and detection logic.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import re
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ from pathlib import Path
15
+
16
+
17
+ def has_git(repo_root: Path | None = None) -> bool:
18
+ """Check if we have git available at the repo root."""
19
+ root = Path(repo_root) if repo_root is not None else Path.cwd()
20
+ git_marker = root / ".git"
21
+ if not (git_marker.is_dir() or git_marker.is_file()):
22
+ return False
23
+ if shutil.which("git") is None:
24
+ return False
25
+ result = subprocess.run(
26
+ ["git", "-C", str(root), "rev-parse", "--is-inside-work-tree"],
27
+ capture_output=True,
28
+ text=True,
29
+ )
30
+ return result.returncode == 0
31
+
32
+
33
+ def effective_branch_name(raw: str) -> str:
34
+ """Strip a single optional path segment (e.g. gitflow "feat/004-name" -> "004-name").
35
+
36
+ Only when the full name is exactly two slash-free segments; otherwise
37
+ returns the raw name.
38
+ """
39
+ match = re.fullmatch(r"([^/]+)/([^/]+)", raw)
40
+ if match:
41
+ return match.group(2)
42
+ return raw
43
+
44
+
45
+ def check_feature_branch(raw: str, has_git_repo: bool) -> bool:
46
+ """Validate that a branch name matches the expected feature branch pattern.
47
+
48
+ Accepts sequential (###-* with >=3 digits) or timestamp (YYYYMMDD-HHMMSS-*)
49
+ formats, either at the start of the branch or after path-style namespace
50
+ prefixes. Logic aligned with the bash/PowerShell twins.
51
+ """
52
+ if not has_git_repo:
53
+ print(
54
+ "[specify] Warning: Git repository not detected; skipped branch validation",
55
+ file=sys.stderr,
56
+ )
57
+ return True
58
+
59
+ branch = effective_branch_name(raw)
60
+ feature_segment = branch.rsplit("/", 1)[-1]
61
+
62
+ # Accept sequential prefix (3+ digits) but exclude malformed timestamps:
63
+ # 7-or-8 digit date + 6-digit time with no trailing slug.
64
+ is_sequential = bool(
65
+ re.match(r"^[0-9]{3,}-", feature_segment)
66
+ and not re.match(r"^[0-9]{7}-[0-9]{6}-", feature_segment)
67
+ and not re.fullmatch(r"[0-9]{7,8}-[0-9]{6}", feature_segment)
68
+ )
69
+ is_timestamp = bool(re.match(r"^[0-9]{8}-[0-9]{6}-", feature_segment))
70
+
71
+ if not is_sequential and not is_timestamp:
72
+ print(f"ERROR: Not on a feature branch. Current branch: {raw}", file=sys.stderr)
73
+ print(
74
+ "Feature branches should be named like: 001-feature-name, "
75
+ "1234-feature-name, 20260319-143022-feature-name, or "
76
+ "<prefix>/001-feature-name",
77
+ file=sys.stderr,
78
+ )
79
+ return False
80
+
81
+ return True
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env python3
2
+ """Git extension: initialize_repo.py
3
+
4
+ Initialize a Git repository with an initial commit.
5
+ Python port of ``initialize-repo.sh`` / ``initialize-repo.ps1``.
6
+ Customizable — replace this script to add .gitignore templates,
7
+ default branch config, git-flow, LFS, signing, etc.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ import shutil
14
+ import subprocess
15
+ import sys
16
+ from pathlib import Path
17
+
18
+
19
+ def _find_project_root(start: Path) -> Path | None:
20
+ current = start
21
+ while True:
22
+ if (current / ".duaer").is_dir() or (current / ".git").exists():
23
+ return current
24
+ if current.parent == current:
25
+ return None
26
+ current = current.parent
27
+
28
+
29
+ def _read_commit_message(repo_root: Path) -> str:
30
+ """Read init_commit_message from git-config.yml, mirroring the bash sed pipeline."""
31
+ default = "[Duaer] Initial commit"
32
+ config_file = repo_root / ".duaer" / "extensions" / "git" / "git-config.yml"
33
+ if not config_file.is_file():
34
+ return default
35
+ try:
36
+ lines = config_file.read_text(encoding="utf-8").splitlines()
37
+ except (OSError, UnicodeDecodeError):
38
+ return default
39
+ for line in lines:
40
+ if line.startswith("init_commit_message:"):
41
+ value = re.sub(r"^init_commit_message:\s*", "", line)
42
+ value = re.sub(r"^[\"']", "", value)
43
+ value = re.sub(r"[\"']*$", "", value)
44
+ if value:
45
+ return value
46
+ return default
47
+
48
+
49
+ def main() -> int:
50
+ script_dir = Path(__file__).resolve().parent
51
+ repo_root = _find_project_root(script_dir) or Path.cwd()
52
+
53
+ commit_msg = _read_commit_message(repo_root)
54
+
55
+ if shutil.which("git") is None:
56
+ print(
57
+ "[specify] Warning: Git not found; skipped repository initialization",
58
+ file=sys.stderr,
59
+ )
60
+ return 0
61
+
62
+ probe = subprocess.run(
63
+ ["git", "rev-parse", "--is-inside-work-tree"],
64
+ cwd=repo_root,
65
+ capture_output=True,
66
+ text=True,
67
+ )
68
+ if probe.returncode == 0:
69
+ print("[specify] Git repository already initialized; skipping", file=sys.stderr)
70
+ return 0
71
+
72
+ steps = [
73
+ (["git", "init", "-q"], "git init"),
74
+ (["git", "add", "."], "git add"),
75
+ (["git", "commit", "--allow-empty", "-q", "-m", commit_msg], "git commit"),
76
+ ]
77
+ for cmd, label in steps:
78
+ result = subprocess.run(cmd, cwd=repo_root, capture_output=True, text=True)
79
+ if result.returncode != 0:
80
+ output = (result.stdout + result.stderr).strip()
81
+ print(f"[specify] Error: {label} failed: {output}", file=sys.stderr)
82
+ return 1
83
+
84
+ print("[OK] Git repository initialized", file=sys.stderr)
85
+ return 0
86
+
87
+
88
+ if __name__ == "__main__":
89
+ raise SystemExit(main())
@@ -0,0 +1,167 @@
1
+ installed:
2
+ - git
3
+ settings:
4
+ auto_execute_hooks: true
5
+ hooks:
6
+ before_constitution:
7
+ - extension: git
8
+ command: duaer.git.initialize
9
+ enabled: true
10
+ optional: false
11
+ priority: 10
12
+ prompt: Execute duaer.git.initialize?
13
+ description: Initialize Git repository before constitution setup
14
+ condition: null
15
+ before_specify:
16
+ - extension: git
17
+ command: duaer.git.feature
18
+ enabled: true
19
+ optional: false
20
+ priority: 10
21
+ prompt: Execute duaer.git.feature?
22
+ description: Create feature branch before specification
23
+ condition: null
24
+ before_clarify:
25
+ - extension: git
26
+ command: duaer.git.commit
27
+ enabled: true
28
+ optional: true
29
+ priority: 10
30
+ prompt: Commit outstanding changes before clarification?
31
+ description: Auto-commit before spec clarification
32
+ condition: null
33
+ before_plan:
34
+ - extension: git
35
+ command: duaer.git.commit
36
+ enabled: true
37
+ optional: true
38
+ priority: 10
39
+ prompt: Commit outstanding changes before planning?
40
+ description: Auto-commit before implementation planning
41
+ condition: null
42
+ before_tasks:
43
+ - extension: git
44
+ command: duaer.git.commit
45
+ enabled: true
46
+ optional: true
47
+ priority: 10
48
+ prompt: Commit outstanding changes before task generation?
49
+ description: Auto-commit before task generation
50
+ condition: null
51
+ before_implement:
52
+ - extension: git
53
+ command: duaer.git.commit
54
+ enabled: true
55
+ optional: true
56
+ priority: 10
57
+ prompt: Commit outstanding changes before implementation?
58
+ description: Auto-commit before implementation
59
+ condition: null
60
+ before_checklist:
61
+ - extension: git
62
+ command: duaer.git.commit
63
+ enabled: true
64
+ optional: true
65
+ priority: 10
66
+ prompt: Commit outstanding changes before checklist?
67
+ description: Auto-commit before checklist generation
68
+ condition: null
69
+ before_analyze:
70
+ - extension: git
71
+ command: duaer.git.commit
72
+ enabled: true
73
+ optional: true
74
+ priority: 10
75
+ prompt: Commit outstanding changes before analysis?
76
+ description: Auto-commit before analysis
77
+ condition: null
78
+ before_taskstoissues:
79
+ - extension: git
80
+ command: duaer.git.commit
81
+ enabled: true
82
+ optional: true
83
+ priority: 10
84
+ prompt: Commit outstanding changes before issue sync?
85
+ description: Auto-commit before tasks-to-issues conversion
86
+ condition: null
87
+ after_constitution:
88
+ - extension: git
89
+ command: duaer.git.commit
90
+ enabled: true
91
+ optional: true
92
+ priority: 10
93
+ prompt: Commit constitution changes?
94
+ description: Auto-commit after constitution update
95
+ condition: null
96
+ after_specify:
97
+ - extension: git
98
+ command: duaer.git.commit
99
+ enabled: true
100
+ optional: true
101
+ priority: 10
102
+ prompt: Commit specification changes?
103
+ description: Auto-commit after specification
104
+ condition: null
105
+ after_clarify:
106
+ - extension: git
107
+ command: duaer.git.commit
108
+ enabled: true
109
+ optional: true
110
+ priority: 10
111
+ prompt: Commit clarification changes?
112
+ description: Auto-commit after spec clarification
113
+ condition: null
114
+ after_plan:
115
+ - extension: git
116
+ command: duaer.git.commit
117
+ enabled: true
118
+ optional: true
119
+ priority: 10
120
+ prompt: Commit plan changes?
121
+ description: Auto-commit after implementation planning
122
+ condition: null
123
+ after_tasks:
124
+ - extension: git
125
+ command: duaer.git.commit
126
+ enabled: true
127
+ optional: true
128
+ priority: 10
129
+ prompt: Commit task changes?
130
+ description: Auto-commit after task generation
131
+ condition: null
132
+ after_implement:
133
+ - extension: git
134
+ command: duaer.git.commit
135
+ enabled: true
136
+ optional: true
137
+ priority: 10
138
+ prompt: Commit implementation changes?
139
+ description: Auto-commit after implementation
140
+ condition: null
141
+ after_checklist:
142
+ - extension: git
143
+ command: duaer.git.commit
144
+ enabled: true
145
+ optional: true
146
+ priority: 10
147
+ prompt: Commit checklist changes?
148
+ description: Auto-commit after checklist generation
149
+ condition: null
150
+ after_analyze:
151
+ - extension: git
152
+ command: duaer.git.commit
153
+ enabled: true
154
+ optional: true
155
+ priority: 10
156
+ prompt: Commit analysis results?
157
+ description: Auto-commit after analysis
158
+ condition: null
159
+ after_taskstoissues:
160
+ - extension: git
161
+ command: duaer.git.commit
162
+ enabled: true
163
+ optional: true
164
+ priority: 10
165
+ prompt: Commit after syncing issues?
166
+ description: Auto-commit after tasks-to-issues conversion
167
+ condition: null
@@ -0,0 +1,9 @@
1
+ {
2
+ "ai": "cursor-agent",
3
+ "ai_skills": true,
4
+ "feature_numbering": "sequential",
5
+ "here": true,
6
+ "integration": "cursor-agent",
7
+ "script": "sh",
8
+ "speckit_version": "1.0.4"
9
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": "1.0.4",
3
+ "integration_state_schema": 1,
4
+ "installed_integrations": [
5
+ "cursor-agent"
6
+ ],
7
+ "integration_settings": {
8
+ "cursor-agent": {
9
+ "script": "sh",
10
+ "invoke_separator": "-"
11
+ }
12
+ },
13
+ "integration": "cursor-agent",
14
+ "default_integration": "cursor-agent"
15
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "integration": "cursor-agent",
3
+ "version": "1.0.4",
4
+ "installed_at": "2026-09-03T21:13:31.261739+00:00",
5
+ "files": {
6
+ ".cursor/skills/duaer-analyze/SKILL.md": "7deb401f0e53be0264722db1c2d401b279ca64ad3fc886a186de39f7ffd5be42",
7
+ ".cursor/skills/duaer-clarify/SKILL.md": "e1a1ddba9346721f41e7b8d0ad278b54d69399c0625db9d753756fd33e51a6e1",
8
+ ".cursor/skills/duaer-constitution/SKILL.md": "442e1c84f9a9f2267700cf08157b2aff65e00abae687689f639ce8f62ff8b6f5",
9
+ ".cursor/skills/duaer-implement/SKILL.md": "df979ca34543fbde2336626752ef72020aa68f4b38c2d108a2da4473113977db",
10
+ ".cursor/skills/duaer-converge/SKILL.md": "aca98c2c55ba124dbddc467642dc90c673f7657ff750b9a93d89916058dded0d",
11
+ ".cursor/skills/duaer-plan/SKILL.md": "3d13e010accc60766db3549a52f598e643f5f6c26570a475df418c7a74ba4cbb",
12
+ ".cursor/skills/duaer-checklist/SKILL.md": "057a2bd2cc91184a6a0b3b2e0fcf6704d4ef55cc1c5939b40e1968431ee10061",
13
+ ".cursor/skills/duaer-specify/SKILL.md": "d0b83970b14be40bd1dcd5d3c15f3186c37ebebbe4617dda7e8d487763b70f79",
14
+ ".cursor/skills/duaer-tasks/SKILL.md": "24e873a5a7adb75e832b6100d6c9140b0a0df4b9650ba32b8102e37697332beb",
15
+ ".cursor/skills/duaer-taskstoissues/SKILL.md": "225297f98e77807dd261ea5692ca0a5b286902d7c9b4855fc30b93f4283ce0a6"
16
+ }
17
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "integration": "duaer",
3
+ "version": "1.0.4",
4
+ "installed_at": "2026-09-03T21:13:31.277502+00:00",
5
+ "files": {
6
+ ".duaer/scripts/bash/common.sh": "170e91ece502b88d83c715e427473843e0b358f550e86e3ff524546df405a9ca",
7
+ ".duaer/scripts/bash/setup-plan.sh": "f417e1b8de7a48fa9d5fea3aabea8fe62149fa11c4b41b3103de06efe73dec8d",
8
+ ".duaer/scripts/bash/setup-tasks.sh": "4a33dd1e6c32ddc7d570f4b538572c54069192573eed0ec654d9fb826e31a4fe",
9
+ ".duaer/scripts/bash/check-prerequisites.sh": "daa377146db4fb69912f42611a7b91c5d55873b3e3e27ed33bd8d67505826344",
10
+ ".duaer/scripts/bash/resolve-template.sh": "829e227096abc8bf0889889ec9f792f503ca5d395b7836a8a7eb739ee75214e7",
11
+ ".duaer/scripts/bash/create-new-feature.sh": "fe99ea8da184380056ce8512ca5d37f67fb499ee4234835c15dcf7c4fb75451c",
12
+ ".duaer/templates/constitution-template.md": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
13
+ ".duaer/templates/checklist-template.md": "856532b3cb66171c662cc16f16b31a5856e4655a8666aad1e545bbfc7f603ca1",
14
+ ".duaer/templates/tasks-template.md": "fc29a233f6f5a27ca31f1aa46b596af6500c627441c6e62b2bc4a1d721525842",
15
+ ".duaer/templates/spec-template.md": "3945437fc35cd30a5b2bf7beea680337c3516826d3efa5a6b92c4a7eca1ba28e",
16
+ ".duaer/templates/plan-template.md": "7e637502d41eccf0ca672496636365691fdca62ef37b27ec07fcb412dbfa90d4",
17
+ ".duaer/.gitignore": "8c908410d177a1ef3d0dee16d7ad55f2ac3333df3104c4d4adee1c9b82f1dbc1"
18
+ }
19
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "sha256": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
3
+ "source": "core"
4
+ }
@@ -0,0 +1,37 @@
1
+ # Project Constitution (template)
2
+
3
+ Replace this file per project. Keep it short and enforceable.
4
+
5
+ Part of **duaer-spec**. If the host repo's `AGENTS.md` conflicts with this
6
+ constitution on isolation, commits, or Issue/PR gates, follow `AGENTS.md`; keep
7
+ Duaer for what to build.
8
+
9
+ ## Core Principles
10
+
11
+ ### I. Spec before code (NON-NEGOTIABLE)
12
+
13
+ Features, architecture changes, and hotfixes start with a Duaer feature
14
+ artifact before implementation. Default path:
15
+
16
+ `constitution` → `specify` → (optional `clarify`) → `plan` → (optional `checklist` / `analyze`) → `tasks` → `implement` → `converge`
17
+
18
+ Hotfix may shorten to: `specify` (mark hotfix) → `tasks` → `implement` → `converge`.
19
+ Never skip `specify` or `converge`.
20
+
21
+ ### II. Read as-is before changing
22
+
23
+ Before any feature work, read:
24
+
25
+ 1. This constitution
26
+ 2. `.duaer/memory/project-context.md` (implementation truth)
27
+ 3. Project product intent doc (if any)
28
+
29
+ ### III. Smallest coherent change
30
+
31
+ Prefer the smallest change that satisfies the acceptance criteria in `spec.md`.
32
+ Do not expand scope without updating the spec and tasks.
33
+
34
+ ### IV. Verify before done
35
+
36
+ Mark tasks complete only when the stated verification (tests, manual checks,
37
+ or converge) has been performed or explicitly waived in the feature docs.
@@ -0,0 +1,24 @@
1
+ # Project context (as-is)
2
+
3
+ Fill this in for each project. Agents treat it as implementation truth.
4
+
5
+ ## Stack
6
+
7
+ - Language / runtime:
8
+ - Package manager:
9
+ - App entrypoints:
10
+ - Test commands:
11
+
12
+ ## Layout
13
+
14
+ - Source roots:
15
+ - Important packages / apps:
16
+
17
+ ## Non-negotiable boundaries
18
+
19
+ - Do not:
20
+ - Always:
21
+
22
+ ## Known gotchas
23
+
24
+ -
@@ -0,0 +1,14 @@
1
+ # Testing expectations (template)
2
+
3
+ ## Levels
4
+
5
+ - L0: lint / typecheck
6
+ - L1: unit tests for changed modules
7
+ - L2: integration tests for changed boundaries
8
+ - L3: e2e / browser for user-visible paths
9
+ - L4: manual account verification when UI or auth is involved
10
+
11
+ ## Default for features
12
+
13
+ Specs and `tasks.md` must include verification tasks. Prefer automated checks;
14
+ document any manual step that cannot be automated.