acstack 0.49.1 → 0.50.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 (41) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +27 -35
  3. package/agents/codex/frontend-verifier.toml +8 -5
  4. package/agents/codex/task-checker.toml +43 -173
  5. package/agents/codex/task-executor.toml +18 -88
  6. package/agents/codex/test-writer.toml +18 -26
  7. package/agents/frontend-verifier.md +8 -5
  8. package/agents/task-checker.md +43 -173
  9. package/agents/task-executor.md +18 -88
  10. package/agents/test-writer.md +18 -26
  11. package/assets/terminal-workspace/lib/commands.sh +7 -0
  12. package/assets/terminal-workspace/lib/pi-packages.sh +29 -0
  13. package/assets/terminal-workspace/pi/acstack-question-blocked.ts +31 -0
  14. package/assets/terminal-workspace/setup.sh +1 -0
  15. package/cli/dist/index.js +0 -2
  16. package/cli/dist/index.js.map +2 -2
  17. package/cli/package.json +1 -1
  18. package/docs/config-reference.md +31 -50
  19. package/docs/terminal-workspace.md +10 -1
  20. package/docs/workflows.md +28 -66
  21. package/orchestrator/AGENTS.md +144 -271
  22. package/package.json +1 -1
  23. package/pi/agents/frontend-verifier.md +8 -5
  24. package/pi/agents/task-checker.md +43 -173
  25. package/pi/agents/task-executor.md +20 -88
  26. package/pi/agents/test-writer.md +18 -26
  27. package/skills/browser-verify/SKILL.md +2 -2
  28. package/skills/grilling/SKILL.md +9 -10
  29. package/skills/implement/SKILL.md +23 -90
  30. package/skills/implement/decision-log.md +1 -1
  31. package/skills/implement/design-pass.md +159 -21
  32. package/skills/map/SKILL.md +4 -7
  33. package/skills/pre-commit-review/SKILL.md +24 -24
  34. package/skills/pre-commit-review/auto-fix.md +1 -1
  35. package/skills/pre-commit-review/interactive.md +1 -1
  36. package/skills/quick-fix/SKILL.md +20 -22
  37. package/skills/setup/SKILL.md +2 -2
  38. package/skills/setup/first-run.md +3 -12
  39. package/skills/setup/report.md +5 -10
  40. package/skills/setup/row-fixes.md +3 -6
  41. package/skills/implement/relaxed-tdd.md +0 -28
@@ -1,33 +1,25 @@
1
1
  ---
2
2
  name: test-writer
3
- description: Writes tests from requirements ONLY. Cannot see implementation plans. Ensures tests encode WHAT should happen, not HOW it's implemented. Context-isolated TDD Phase 1 under strict TDD; under relaxed TDD it runs after the executor and still writes from requirements only.
3
+ description: Optional independent test author who derives assertions from requirements and interfaces, not implementation plans or current implementation answers.
4
4
  model: opus
5
5
  color: green
6
6
  ---
7
7
 
8
- # Test writer (TDD phase 1)
8
+ # Test writer
9
9
 
10
10
  You write tests from requirements and application programming interface (API) contracts only. You do not receive implementation plans or architectural decisions. Your tests encode what should happen, not how the executor implements it.
11
11
 
12
12
  ## When you run
13
13
 
14
- `workflow.tdd.strict` in `acstack/project.yaml` decides where you sit in the pipeline. The key reads as `true` unless the file says `false`.
14
+ - Follow the shared Implementation discipline in `orchestrator/AGENTS.md`
15
+ - You run when independent requirements interpretation adds confidence or the user requests separate test ownership
16
+ - Read `test-conventions` before writing tests
17
+ - Derive expectations from requirements and existing interfaces, never implementation bodies
18
+ - Read signatures, exported types, route paths, and schemas only to learn how to call the subject
19
+ - Prefer intended failing-first evidence when practical
20
+ - If implementation already exists, leave it unread and report the available evidence honestly
21
+ - Report mismatches instead of reshaping assertions to fit the code
15
22
 
16
- Under `true` you run first. No implementation exists yet, so every test you write must fail. Verify that before you report.
17
-
18
- Under `false`, relaxed TDD, you run after the task-executor. The implementation already sits in the working tree.
19
-
20
- Write your tests from the requirements and the existing interfaces. Do not read the implementation you are testing.
21
-
22
- An interface here means a signature, an exported type, a route path or a schema. It never means a function body.
23
-
24
- Reading a body to learn what the code does, then asserting that, writes a test that can only agree with the code. Your licence to read source is for calling the thing, never for learning what it decided.
25
-
26
- Then verify the tests pass, because the code is already there.
27
-
28
- A test that fails under `false` means the implementation is wrong. Report it. Never reshape the test to fit the code.
29
-
30
- Requirements alone are a weaker guarantee than the strict order gives, where no implementation exists for you to read. That weakness is the cost of relaxed TDD, and your own discipline is the only thing that holds it down.
31
23
 
32
24
  ## Project configuration
33
25
 
@@ -68,9 +60,9 @@ A narrower test must still fail when the criterion is unmet. This is the floor,
68
60
 
69
61
  A test that passes against any implementation is not narrower. It is absent, and it leaves the criterion untested.
70
62
 
71
- `assert create_plan(...) is not None` shows the trap, and neither setting catches it for you. Under strict TDD it fails today because the function is missing, then passes forever after. Under relaxed TDD it passes the moment you write it, and that green tick tells you nothing.
63
+ `assert create_plan(...) is not None` can pass for an incorrect result. A passing test alone does not establish useful coverage.
72
64
 
73
- So check the floor yourself, whichever order you ran in. Ask what an implementation would have to get wrong for this test to fail. If you cannot name that, the test is vacuous.
65
+ Check the floor yourself. Ask what an implementation would have to get wrong for this test to fail. If you cannot name that, the test is vacuous.
74
66
 
75
67
  The quickest form of that check: would this test still pass if every function it imports returned `undefined`? If yes, it is vacuous. Five shapes fail it:
76
68
 
@@ -102,14 +94,15 @@ Assert only what the acceptance criteria state. A convention you found in a skil
102
94
 
103
95
  ## File path constraint
104
96
 
105
- - Create or modify files only in the test directory (from `directories.tests` in project.yaml)
106
- - Verify the expected result: all tests fail under strict TDD, and all pass under relaxed (see When you run)
97
+ - Create or modify only assigned test files, using project test placement conventions
98
+ - Confirm intended failures when testing first, then report passing results when implementation is available
107
99
  - Use shared fixtures where they exist (e.g., conftest.py for pytest)
108
100
  - Add new reusable fixtures to shared fixture files
109
101
 
110
102
  ## Test organization rules (critical)
111
103
 
112
- Mirror the source directory structure. Tests for `{source}/services/foo.py` go in `{tests}/services/test_foo.py`.
104
+ Follow project conventions and `test-conventions` for test placement, including colocated frontend tests.
105
+ For mirrored backend tests, `{source}/services/foo.py` maps to `{tests}/services/test_foo.py`.
113
106
 
114
107
  Rules:
115
108
 
@@ -124,7 +117,7 @@ Rules:
124
117
  1. Read requirements and acceptance criteria
125
118
  2. Discover existing interfaces using code search
126
119
  3. Write the narrowest test each criterion permits, and confirm it would fail on a wrong implementation
127
- 4. Run the test command. Under strict every test must fail. Under relaxed every test must pass
120
+ 4. Run the test command and report intended failures, passing checks, or behavior mismatches
128
121
  5. Report: list of test files, test names, the expected result you verified, requirements coverage
129
122
 
130
123
  ## Test quality standards
@@ -142,7 +135,6 @@ When complete, report:
142
135
 
143
136
  - List of test files created/modified
144
137
  - Test names and what requirement each covers
145
- - Confirmation of the expected result, with test output: all failing under strict, all passing under relaxed
146
- - Which setting `workflow.tdd.strict` held, and so which order you ran in
138
+ - Commands and results, including available failing-first evidence and any tests-first departure
147
139
  - Any criterion you could not test without picking an answer, and the question that settles it
148
140
  - Any criterion you could not test for another reason, and what stopped you
@@ -58,6 +58,13 @@ acstack_tw_main() {
58
58
  for id in "$PI_ACSTACK" $(version_table | awk -F'\t' '$2=="pi" && $3=="npm" && $1!="pi"{print $1}' | while read -r name; do pi_npm_package "$name"; done); do
59
59
  src=$(pi_state pkg "$id") && say "pi package: $src" || say "pi package: $id not installed"
60
60
  done
61
+ if [ -e "$PI_QUESTION_BRIDGE" ] || [ -L "$PI_QUESTION_BRIDGE" ]; then
62
+ pi_state owns file "$PI_QUESTION_BRIDGE" \
63
+ && say "pi question blocked bridge: managed ($PI_QUESTION_BRIDGE)" \
64
+ || say "pi question blocked bridge: user-owned path ($PI_QUESTION_BRIDGE)"
65
+ else
66
+ say "pi question blocked bridge: absent ($PI_QUESTION_BRIDGE)"
67
+ fi
61
68
  [ -e "$PI_RETRO_EXT" ] && say "pi agentic-retro extension: present" \
62
69
  || say "pi agentic-retro extension: absent"
63
70
  grep -qsF "$PI_MD_BEGIN" "$PI_DIR"/AGENTS.override.md "$PI_DIR"/AGENTS.md "$PI_DIR"/AGENTS.MD \
@@ -140,6 +140,32 @@ bili_client() { # bili_client install|remove <client>
140
140
  fi
141
141
  }
142
142
 
143
+ pi_question_bridge() { # pi_question_bridge install|remove
144
+ local fresh=0 rc
145
+ if [ "$1" = "install" ]; then
146
+ if { [ -e "$PI_QUESTION_BRIDGE" ] || [ -L "$PI_QUESTION_BRIDGE" ]; } \
147
+ && ! pi_state owns file "$PI_QUESTION_BRIDGE"; then
148
+ say "pi: $PI_QUESTION_BRIDGE already exists, so the question bridge was left alone"
149
+ return 0
150
+ fi
151
+ [ -e "$PI_QUESTION_BRIDGE" ] || [ -L "$PI_QUESTION_BRIDGE" ] || fresh=1
152
+ mkdir -p "$(dirname "$PI_QUESTION_BRIDGE")" \
153
+ && cp "$SCRIPT_DIR/pi/acstack-question-blocked.ts" "$PI_QUESTION_BRIDGE" || return $?
154
+ if pi_state own file "$PI_QUESTION_BRIDGE"; then
155
+ say "pi: question blocked bridge written to $PI_QUESTION_BRIDGE"
156
+ else
157
+ rc=$?
158
+ [ "$fresh" = 1 ] && rm -f "$PI_QUESTION_BRIDGE"
159
+ say "pi: question blocked bridge ownership registration failed for $PI_QUESTION_BRIDGE"
160
+ return "$rc"
161
+ fi
162
+ elif pi_state owns file "$PI_QUESTION_BRIDGE"; then
163
+ rm -f "$PI_QUESTION_BRIDGE" \
164
+ && pi_state disown file "$PI_QUESTION_BRIDGE" \
165
+ && say "pi: question blocked bridge removed"
166
+ fi
167
+ }
168
+
143
169
  apply_pi() {
144
170
  find_pi
145
171
  if ! have pi; then
@@ -195,7 +221,9 @@ apply_pi() {
195
221
  fi
196
222
  if [ "$(cfg_get pi.question_tool true)" = "true" ]; then
197
223
  pi_add npm:@juicesharp/rpiv-ask-user-question npm:@juicesharp/rpiv-ask-user-question
224
+ pi_question_bridge install
198
225
  else
226
+ pi_question_bridge remove
199
227
  pi_drop npm:@juicesharp/rpiv-ask-user-question
200
228
  fi
201
229
  if [ "$(cfg_get pi.web_access true)" = "true" ]; then
@@ -283,6 +311,7 @@ apply_pi() {
283
311
  # and leaves the pi binary, its logins and its sessions alone.
284
312
  remove_pi() {
285
313
  find_pi
314
+ pi_question_bridge remove
286
315
  if pi_state parses "$PI_SETTINGS"; then
287
316
  pi_unpin "$PI_ACSTACK"
288
317
  pi_drop "$PI_ACSTACK"
@@ -0,0 +1,31 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+
3
+ export default function (pi: ExtensionAPI) {
4
+ let pending: { active: boolean; native: boolean } | undefined;
5
+ let forwarding = false;
6
+
7
+ pi.events.on("herdr:blocked", (event: unknown) => {
8
+ if (!forwarding && pending && (event as { active?: unknown } | null)?.active === pending.active) {
9
+ pending.native = true;
10
+ pending = undefined;
11
+ }
12
+ });
13
+
14
+ pi.events.on("rpiv:ask-user:blocked", (event: unknown) => {
15
+ const active = (event as { active?: unknown } | null)?.active;
16
+ if (typeof active !== "boolean") return;
17
+ const transition = { active, native: false };
18
+ pending = transition;
19
+ // shortcut: dual producers emit public then herdr synchronously; async pairs need an upstream source ID
20
+ queueMicrotask(() => {
21
+ if (pending === transition) pending = undefined;
22
+ if (transition.native) return;
23
+ forwarding = true;
24
+ try {
25
+ pi.events.emit("herdr:blocked", { active });
26
+ } finally {
27
+ forwarding = false;
28
+ }
29
+ });
30
+ });
31
+ }
@@ -41,6 +41,7 @@ END="# <<< acstack terminal-workspace <<<"
41
41
  PI_DIR="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
42
42
  PI_SETTINGS="$PI_DIR/settings.json"
43
43
  PI_KEYBINDINGS="$PI_DIR/keybindings.json"
44
+ PI_QUESTION_BRIDGE="$PI_DIR/extensions/acstack-question-blocked.ts"
44
45
  PI_SUBAGENT_CFG="$PI_DIR/extensions/subagent/config.json"
45
46
  # agentic-retro install-pi picks its own directory, from $PI_HOME rather than
46
47
  # pi's override, so look where it writes.
package/cli/dist/index.js CHANGED
@@ -12459,8 +12459,6 @@ var init_project_config = __esm({
12459
12459
  PROJECT_KEY_DEFAULTS = {
12460
12460
  "workflow.spec_sync": "true",
12461
12461
  "workflow.grilling.questions": "tool",
12462
- "workflow.tdd.strict": "true",
12463
- "workflow.tdd.orchestrator_strict": "true",
12464
12462
  [FRONTEND_VERIFICATION_KEY]: "false",
12465
12463
  [DESIGN_REVIEW_KEY]: "false",
12466
12464
  "workflow.maps.persist": "false",