acstack 0.0.0-stage → 0.47.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.
- package/.claude-plugin/plugin.json +19 -0
- package/LICENSE +21 -0
- package/README.md +389 -2
- package/THIRD_PARTY_NOTICES +14 -0
- package/TROUBLESHOOTING.md +225 -0
- package/agents/codex/frontend-verifier.toml +352 -0
- package/agents/codex/task-checker.toml +345 -0
- package/agents/codex/task-executor.toml +157 -0
- package/agents/codex/test-writer.toml +149 -0
- package/agents/frontend-verifier.md +351 -0
- package/agents/task-checker.md +344 -0
- package/agents/task-executor.md +156 -0
- package/agents/test-writer.md +148 -0
- package/assets/playwriter-bridge/build-launcher.ps1 +32 -0
- package/assets/playwriter-bridge/doctor.ps1 +450 -0
- package/assets/playwriter-bridge/launcher-src.cs +131 -0
- package/assets/playwriter-bridge/opener.js +330 -0
- package/assets/playwriter-bridge/opener.ps1 +63 -0
- package/assets/playwriter-bridge/register-tasks.ps1 +328 -0
- package/assets/playwriter-bridge/relay.ps1 +50 -0
- package/assets/playwriter-bridge/stop.ps1 +125 -0
- package/assets/playwriter-bridge/sync.ps1 +271 -0
- package/assets/playwriter-bridge/tunnel.ps1 +325 -0
- package/assets/playwriter-bridge/vm-free-port.sh +166 -0
- package/assets/terminal-workspace/client-keybindings.md +87 -0
- package/assets/terminal-workspace/config.example.yaml +329 -0
- package/assets/terminal-workspace/setup.sh +4317 -0
- package/cli/README.md +47 -0
- package/cli/bin/acstack.js +3 -0
- package/cli/dist/index.js +25984 -0
- package/cli/dist/index.js.map +7 -0
- package/cli/package.json +59 -0
- package/docs/adr/0001-ste-writing-standard.md +16 -0
- package/docs/adr/0002-version-gate-not-auto-bump.md +35 -0
- package/docs/adr/0003-handoff-ack-command.md +19 -0
- package/docs/adr/0004-dev-setup-per-host.md +13 -0
- package/docs/adr/0005-two-orchestrators-per-host.md +25 -0
- package/docs/adr/0006-document-opener-service.md +89 -0
- package/docs/adr/0007-lead-check-reads-the-transcript.md +87 -0
- package/docs/adr/0008-cli-audits-skill-interviews.md +37 -0
- package/docs/adr/0009-verify-the-write-not-the-writer.md +51 -0
- package/docs/adr/0010-promote-dev-to-main-by-fast-forward.md +115 -0
- package/docs/adr/0011-node-header-is-the-edge-encoding.md +20 -0
- package/docs/adr/0012-node-kind-is-a-closed-set-of-five.md +31 -0
- package/docs/adr/0013-the-diagram-renderer-is-pure.md +23 -0
- package/docs/adr/0014-reading-a-node-hides-its-generated-diagram.md +29 -0
- package/docs/adr/0015-a-node-id-links-to-its-issue-through-an-anchor.md +37 -0
- package/docs/adr/0016-the-map-root-carries-no-status-of-its-own.md +21 -0
- package/docs/adr/0017-call-impeccable-copy-only-its-questions.md +13 -0
- package/docs/adr/index.md +17 -0
- package/docs/config-reference.md +572 -0
- package/docs/dev-setups.md +164 -0
- package/docs/migrate-0.8.md +28 -0
- package/docs/playwriter-bridge-windows.md +15 -0
- package/docs/terminal-workspace-keys.md +137 -0
- package/docs/terminal-workspace.md +1774 -0
- package/docs/workflows.md +309 -0
- package/hooks/ac-writing-style-reminder.sh +86 -0
- package/hooks/context-pressure.sh +583 -0
- package/hooks/hooks.json +50 -0
- package/hooks/session-start-handoff.sh +165 -0
- package/hooks/session-start.sh +214 -0
- package/orchestrator/AGENTS.md +532 -0
- package/output-styles/ac-writing-style.md +286 -0
- package/package.json +50 -4
- package/pi/agents/frontend-verifier.md +357 -0
- package/pi/agents/task-checker.md +348 -0
- package/pi/agents/task-executor.md +160 -0
- package/pi/agents/test-writer.md +154 -0
- package/pi/extension.ts +723 -0
- package/profiles/fullstack-python.yaml +29 -0
- package/profiles/node-typescript.yaml +23 -0
- package/profiles/python.yaml +24 -0
- package/skills/archive/SKILL.md +160 -0
- package/skills/bootstrap/SKILL.md +109 -0
- package/skills/bridge/HANDOFF.md +93 -0
- package/skills/bridge/SKILL-VM.md +292 -0
- package/skills/bridge/SKILL-WINDOWS.md +526 -0
- package/skills/bridge/SKILL.md +121 -0
- package/skills/browser-verify/SKILL.md +229 -0
- package/skills/code-review/SKILL.md +44 -0
- package/skills/commit/SKILL.md +106 -0
- package/skills/compact-and-continue/SKILL.md +241 -0
- package/skills/explore/SKILL.md +149 -0
- package/skills/grilling/SKILL.md +134 -0
- package/skills/handoff/SKILL.md +250 -0
- package/skills/handoff/launch-herdr.md +197 -0
- package/skills/handoff/launch-without-herdr.md +30 -0
- package/skills/handoff/lead-check.md +59 -0
- package/skills/handoff/outcomes.md +162 -0
- package/skills/handoff/running-agent.md +50 -0
- package/skills/handoff/watch.md +115 -0
- package/skills/host/SKILL.md +247 -0
- package/skills/host/detect-host.sh +297 -0
- package/skills/host/herdr.md +79 -0
- package/skills/host/install-herdr-block.sh +467 -0
- package/skills/host/install-orca.sh +512 -0
- package/skills/host/notes.md +97 -0
- package/skills/host/orca.md +160 -0
- package/skills/host/questions.md +163 -0
- package/skills/host/report.md +42 -0
- package/skills/implement/SKILL.md +230 -0
- package/skills/implement/decision-log.md +62 -0
- package/skills/implement/design-pass.md +32 -0
- package/skills/implement/relaxed-tdd.md +28 -0
- package/skills/map/SKILL.md +250 -0
- package/skills/map/frontend.md +227 -0
- package/skills/map/trackers/files.md +74 -0
- package/skills/map/trackers/github.md +180 -0
- package/skills/map/visuals.md +387 -0
- package/skills/new-worktree/SKILL.md +155 -0
- package/skills/new-worktree/detect-orchestrator.sh +328 -0
- package/skills/new-worktree/detect-orchestrator.test.sh +894 -0
- package/skills/new-worktree/herdr.md +84 -0
- package/skills/new-worktree/orca.md +101 -0
- package/skills/pre-commit-review/SKILL.md +242 -0
- package/skills/pre-commit-review/auto-fix.md +19 -0
- package/skills/pre-commit-review/interactive.md +35 -0
- package/skills/quick-fix/SKILL.md +125 -0
- package/skills/record-context/SKILL.md +106 -0
- package/skills/setup/SKILL.md +250 -0
- package/skills/setup/browser.md +123 -0
- package/skills/setup/design-review.md +97 -0
- package/skills/setup/first-run.md +114 -0
- package/skills/setup/report.md +68 -0
- package/skills/setup/row-fixes.md +196 -0
- package/skills/setup/terminal-workspace.md +153 -0
- package/skills/sync/SKILL.md +171 -0
- package/skills/teardown-worktree/SKILL.md +245 -0
- package/skills/teardown-worktree/detector.md +39 -0
- package/skills/teardown-worktree/herdr.md +113 -0
- package/skills/teardown-worktree/orca.md +61 -0
- package/skills/terminal-workspace/SKILL.md +249 -0
- package/skills/terminal-workspace/components.md +36 -0
- package/skills/terminal-workspace/config-choices.md +69 -0
- package/skills/terminal-workspace/emulator-keys.md +48 -0
- package/skills/terminal-workspace/headless-clipboard.md +175 -0
- package/skills/terminal-workspace/turn-off-and-update.md +104 -0
- package/skills/test-conventions/SKILL.md +59 -0
- package/skills/visual-docs/SKILL.md +246 -0
- package/skills/writing/SKILL.md +231 -0
- package/skills/writing/examples.md +153 -0
- package/skills/writing/vocabulary.md +96 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
# Project configuration
|
|
2
|
+
|
|
3
|
+
*Every key in `acstack/project.yaml`, the values it takes, its default, and what changes when you change it.*
|
|
4
|
+
|
|
5
|
+
One file, `acstack/project.yaml`, holds every setting that belongs to a project.
|
|
6
|
+
|
|
7
|
+
- **It names what to run**: the test, build, lint, typecheck, and format commands
|
|
8
|
+
- **It names where to look**: the source and test directories, and the frontend
|
|
9
|
+
- **It names how strict to be**: whether tests come first, and whether the agent may write code itself
|
|
10
|
+
- **Every skill and every agent reads it** before it runs anything
|
|
11
|
+
- **A key you leave out takes the default** this document gives it
|
|
12
|
+
|
|
13
|
+
Three terms first.
|
|
14
|
+
|
|
15
|
+
- **A key** is one dotted path into the file, such as `workflow.tdd.strict`
|
|
16
|
+
- **A default** is what ACStack does when the file states no value
|
|
17
|
+
- **A rung** is one token count at which the context-pressure hook speaks up, asking the session to choose continuity or freshness
|
|
18
|
+
|
|
19
|
+
```mermaid
|
|
20
|
+
flowchart TD
|
|
21
|
+
FILE["acstack/project.yaml<br/>(written by /acstack:setup)"] --> WF["workflow.*<br/>switches, maps, handoff"]
|
|
22
|
+
FILE --> RUN["commands.*, directories.*,<br/>test_markers, venv"]
|
|
23
|
+
FILE --> FE["frontend.*<br/>dev server, browser"]
|
|
24
|
+
WF --> SKILLS["commit, grilling, map,<br/>archive, handoff"]
|
|
25
|
+
WF --> HOOK["hooks/context-pressure.sh"]
|
|
26
|
+
RUN --> AGENTS["orchestrator, test-writer,<br/>task-executor, task-checker"]
|
|
27
|
+
FE --> FV["frontend-verifier"]
|
|
28
|
+
HOST["~/.config/acstack/config.json<br/>host.* keys - see dev-setups.md"] --> FV
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Step in the diagram | Detail |
|
|
32
|
+
| --- | --- |
|
|
33
|
+
| The file, and what writes it | section 1 |
|
|
34
|
+
| `profile` | section 2 |
|
|
35
|
+
| `workflow.*` switches | section 3 |
|
|
36
|
+
| `workflow.maps` | section 4 |
|
|
37
|
+
| `workflow.handoff` | sections 5 and 6 |
|
|
38
|
+
| `commands`, `directories`, `test_markers`, `venv` | section 7 |
|
|
39
|
+
| `frontend.*` | sections 8 and 9 |
|
|
40
|
+
| `host.*`, and what is not a key here | section 10 |
|
|
41
|
+
| Every default | section 11 |
|
|
42
|
+
|
|
43
|
+
## 1. Where the file lives, and what writes it
|
|
44
|
+
|
|
45
|
+
*The setup skill writes the whole file. To change one key, run `acstack config set`.*
|
|
46
|
+
|
|
47
|
+
- `acstack/project.yaml` sits in the project root, beside `acstack/domain-map.yaml`
|
|
48
|
+
- `/acstack:setup` writes it
|
|
49
|
+
- On a fresh project the skill detects the project type, then writes every key from a profile under `profiles/`
|
|
50
|
+
- On a project that already has the file, the skill leads with the health check and offers a menu of changes
|
|
51
|
+
- `acstack config set <key> <value>` changes one key without an interview
|
|
52
|
+
- A key the file omits takes the default the tables below give it
|
|
53
|
+
- The `commands`, `directories`, and `frontend` keys have no such default
|
|
54
|
+
- The setup skill copies them from a profile
|
|
55
|
+
- An omitted one names nothing at all
|
|
56
|
+
|
|
57
|
+
Four commands read the file and change nothing.
|
|
58
|
+
|
|
59
|
+
| Command | What it prints |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `acstack config show` | the profile, the commands, and what the project says about its browser |
|
|
62
|
+
| `acstack config check` | an audit of the project and the machine, exiting 2 when a required host axis has no value |
|
|
63
|
+
| `acstack config list` | every key the file states |
|
|
64
|
+
| `acstack design-gate` | whether a design review runs here, as `on` and exit 0 or one `off: <reason>` line per reason and exit 1 (section 3) |
|
|
65
|
+
|
|
66
|
+
### 1.1. Which file a write lands in
|
|
67
|
+
|
|
68
|
+
*The key decides, and the key alone.*
|
|
69
|
+
|
|
70
|
+
- A `host.*` key describes the machine
|
|
71
|
+
- It goes to the global config at `~/.config/acstack/config.json`
|
|
72
|
+
- Every other key in this document describes the project
|
|
73
|
+
- It goes to `acstack/project.yaml`
|
|
74
|
+
- Section 10 covers the `host.*` keys
|
|
75
|
+
- `acstack config get <key>` and `acstack config unset <key>` read the same key table and route the same way
|
|
76
|
+
|
|
77
|
+
### 1.2. What a command refuses
|
|
78
|
+
|
|
79
|
+
*Validation happens before either file opens, so a refusal writes nothing.*
|
|
80
|
+
|
|
81
|
+
- `acstack config set` refuses a value outside the set a table gives
|
|
82
|
+
- It refuses a key neither schema knows, and names the keys it does know
|
|
83
|
+
- It refuses a project key outside an ACStack project
|
|
84
|
+
- No file is there to hold it
|
|
85
|
+
- It holds a numeric key to the range its reader honours, not merely to being a number
|
|
86
|
+
- A count of turns or of tokens takes a whole number of 0 or more
|
|
87
|
+
- A CDP port takes a whole number from 1 to 65535
|
|
88
|
+
- A refusal names the range
|
|
89
|
+
- A value the reader would reject otherwise fails somewhere else entirely, long after the command that accepted it exited 0
|
|
90
|
+
- `get` and `unset` both refuse a key neither schema knows
|
|
91
|
+
- Both refuse a project key outside an ACStack project
|
|
92
|
+
|
|
93
|
+
### 1.3. What `get` prints, and what it exits
|
|
94
|
+
|
|
95
|
+
*One exit code means "no value here", whichever file holds the key.*
|
|
96
|
+
|
|
97
|
+
- A string prints as written, and anything else prints as JSON
|
|
98
|
+
- The command exits 2 when the file states no value
|
|
99
|
+
- An unset `host.*` axis already gives that code
|
|
100
|
+
- A caller branches on "no value here" without knowing which file holds the key
|
|
101
|
+
- The message names the documented default where a table below gives a literal one
|
|
102
|
+
- The message names no default where a table describes the default instead
|
|
103
|
+
- There is none to name
|
|
104
|
+
|
|
105
|
+
### 1.4. What `unset` leaves behind
|
|
106
|
+
|
|
107
|
+
*`acstack config unset` takes the key out. It prunes nothing else.*
|
|
108
|
+
|
|
109
|
+
- Removing a key the file does not state is not an error
|
|
110
|
+
- The command writes nothing at all
|
|
111
|
+
- The file stays byte for byte as it was
|
|
112
|
+
- A block the removal emptied keeps its key with nothing after the colon
|
|
113
|
+
- Every reader treats that as the default
|
|
114
|
+
- The command prunes nothing
|
|
115
|
+
- Pruning would take any comment on that block with it
|
|
116
|
+
|
|
117
|
+
### 1.5. How a write preserves the rest of the file
|
|
118
|
+
|
|
119
|
+
*Two write paths exist. The result of the first decides which one runs.*
|
|
120
|
+
|
|
121
|
+
A write keeps the file as you wrote it. Comments, blank lines, key order, and quoting all survive.
|
|
122
|
+
|
|
123
|
+
- The command first tries to change the key in place, rewriting the bytes of the old value and leaving every other byte alone
|
|
124
|
+
- It then checks its own work
|
|
125
|
+
- It parses the file it just built and compares the data, in full, against the data the second path would have written
|
|
126
|
+
- It keeps the in-place change only when the two agree
|
|
127
|
+
- The write goes through the YAML document instead, when the two disagree or when the file it built does not parse
|
|
128
|
+
- That path carries every comment across
|
|
129
|
+
- It re-emits the whole file
|
|
130
|
+
- A run of spaces before an inline comment shortens to one
|
|
131
|
+
- A collection stated in flow style stays in flow style
|
|
132
|
+
- Line endings survive either path
|
|
133
|
+
- The command writes a file that arrived with CRLF back in CRLF
|
|
134
|
+
- Setting one key never rewrites the ending of every line
|
|
135
|
+
- `acstack config unset` writes the same two ways and checks itself the same way
|
|
136
|
+
- It splices the whole line out, indentation and trailing comment included, then compares the data before it keeps that
|
|
137
|
+
|
|
138
|
+
So the result decides which path a write takes, and never the shape of the key.
|
|
139
|
+
|
|
140
|
+
- A hand-edited file with ordinary one-line values gets the byte-for-byte path
|
|
141
|
+
- That is the common case
|
|
142
|
+
- It is the path such a file needs
|
|
143
|
+
- Anything the check cannot vouch for gets re-emitted
|
|
144
|
+
- The command checks that emission too, because the YAML document interface can itself write a line no parser reads back
|
|
145
|
+
- It costs only formatting when the check passes
|
|
146
|
+
- A write neither path can vouch for gets refused
|
|
147
|
+
- The command exits 1 and names the key
|
|
148
|
+
- The file stays byte for byte as it was
|
|
149
|
+
- Losing the alignment of a comment beats losing the key that follows a block scalar
|
|
150
|
+
|
|
151
|
+
### 1.6. Four file shapes that get a decision rather than a crash
|
|
152
|
+
|
|
153
|
+
*A file ACStack itself produced always reads back.*
|
|
154
|
+
|
|
155
|
+
- A project that holds `acstack/` and no `project.yaml` gets the file created
|
|
156
|
+
- A block stating nothing takes a key underneath it, whether the file spells that block as a bare colon, as `null` or as `~`
|
|
157
|
+
- That is the state `acstack config unset` leaves behind
|
|
158
|
+
- The tool accepts its own output
|
|
159
|
+
- A document stating nothing at all takes a key too
|
|
160
|
+
- A file holding only `---` works the way an empty file already does
|
|
161
|
+
- The command refuses a key holding a value where the path wants a block
|
|
162
|
+
- It names that key
|
|
163
|
+
- Writing there would mean discarding the value already in the file
|
|
164
|
+
- That value is yours rather than the command's
|
|
165
|
+
- The file stays byte for byte as it was
|
|
166
|
+
|
|
167
|
+
Three refusals protect a file neither command can handle.
|
|
168
|
+
|
|
169
|
+
- Both `set` and `unset` refuse a file whose bytes are not valid UTF-8
|
|
170
|
+
- Neither one can re-encode a byte it could not decode
|
|
171
|
+
- A write would stand the replacement character in its place and report success
|
|
172
|
+
- Both refuse a file the process may not write
|
|
173
|
+
- Both refuse a document that is a scalar or a list, because no key hangs under either
|
|
174
|
+
- Both name the file and the reason
|
|
175
|
+
|
|
176
|
+
### 1.7. What `acstack config list` reports
|
|
177
|
+
|
|
178
|
+
*The listing answers what the file states, and `get` answers what the schema knows.*
|
|
179
|
+
|
|
180
|
+
- The listing prints every key the file states, in the file's own order
|
|
181
|
+
- A stated key no schema knows carries `(unknown key)`
|
|
182
|
+
- `acstack config get` refuses that key
|
|
183
|
+
- `acstack config set` will not write it
|
|
184
|
+
- A block header holding nothing carries no mark where the schema knows keys under it
|
|
185
|
+
- `acstack config unset` leaves that line standing when it removes a block's last key
|
|
186
|
+
- The tool wrote `workflow:` itself, so the listing does not warn about it
|
|
187
|
+
- A key holding a dot is a key of its own
|
|
188
|
+
- YAML reads `"workflow.spec_sync": true` as one key whose name contains a dot
|
|
189
|
+
- The same file may state the nested spelling as well
|
|
190
|
+
- The listing prints both rows, and marks the literal one
|
|
191
|
+
- Every command walks the blocks, so all of them answer out of the nested spelling
|
|
192
|
+
- The `--json` output gives the project side as a list of rows, in the file's own order
|
|
193
|
+
- Each row reads `{"key": "<dotted path>", "value": ..., "known": true|false}`
|
|
194
|
+
- The rows are a list, so two rows can share one dotted path
|
|
195
|
+
|
|
196
|
+
## 2. profile
|
|
197
|
+
|
|
198
|
+
*One word naming the defaults the file started from.*
|
|
199
|
+
|
|
200
|
+
| Key | Values | Default | What changes |
|
|
201
|
+
| --- | --- | --- | --- |
|
|
202
|
+
| `profile` | `node-typescript`, `python`, `fullstack-python` | none | `/acstack:setup` reads `profiles/<profile>.yaml` for its proposed commands and directories; `acstack config show` prints the name |
|
|
203
|
+
|
|
204
|
+
- The profile decides nothing after setup has run
|
|
205
|
+
- The commands and directories it proposed live in the file as their own keys, and those keys are what every later reader uses
|
|
206
|
+
|
|
207
|
+
## 3. workflow: the switches
|
|
208
|
+
|
|
209
|
+
*Five booleans sit in the block. Each one changes how much discipline ACStack applies. A sixth key decides how the agent asks you a question.*
|
|
210
|
+
|
|
211
|
+
| Key | Values | Default | What changes |
|
|
212
|
+
| --- | --- | --- | --- |
|
|
213
|
+
| `workflow.spec_sync` | `true`, `false` | `true` | the `commit` skill runs spec sync before it commits, unless this says `false`; with `false` the living specs under `acstack/specs/` stop tracking the code |
|
|
214
|
+
| `workflow.tdd.strict` | `true`, `false` | `true` | `false` runs the task-executor before the test-writer; all three phases still run, and the checker's gate changes with the key |
|
|
215
|
+
| `workflow.tdd.orchestrator_strict` | `true`, `false` | `true` | `false` lets the coordinating session read and write code itself for small tasks, instead of handing every piece to a subagent |
|
|
216
|
+
| `workflow.frontend_verification` | `true`, `false` | `false` | the frontend-verifier runs only when this is `true`, the file configures a `frontend` block, and the change touched frontend files |
|
|
217
|
+
| `workflow.design_review` | `true`, `false` | `false` | a design review runs on a UI change only when this is `true`; it needs the impeccable plugin installed and switched on, which `acstack config check` reports on |
|
|
218
|
+
|
|
219
|
+
How a boolean reads:
|
|
220
|
+
|
|
221
|
+
- Every boolean key in this document takes the same six spellings as a `host.*` axis, and case does not matter
|
|
222
|
+
- `true`, `yes`, and `on` all read as true
|
|
223
|
+
- `false`, `no`, and `off` all read as false
|
|
224
|
+
- The tables show `true` and `false`
|
|
225
|
+
- That is what the file holds after `acstack config set` writes one
|
|
226
|
+
- `workflow.spec_sync` reads as on unless the file states one of the three false spellings outright
|
|
227
|
+
- The `commit` skill treats a missing key and an explicit `true` the same way
|
|
228
|
+
- `workflow.frontend_verification` reads as off unless the file states one of the three true spellings outright
|
|
229
|
+
- The frontend-verifier is the only reader
|
|
230
|
+
- `orchestrator/AGENTS.md` and the `browser-verify` skill both run it on an explicit `true` alone
|
|
231
|
+
- The defaults block in section 11 shows `true` for this key
|
|
232
|
+
- That is what `/acstack:setup` writes for a project with a frontend
|
|
233
|
+
- That is what setup writes, and not what an absent key means
|
|
234
|
+
- `workflow.design_review` reads as off unless the file states one of the three true spellings outright
|
|
235
|
+
- `acstack design-gate` is the one command that answers whether the gate is on
|
|
236
|
+
- On needs both halves: the key true, and the impeccable plugin installed
|
|
237
|
+
- A plugin set `false` under `enabledPlugins` in `settings.json` counts as not installed
|
|
238
|
+
- It reads this file, the plugin registry, and `settings.json`, runs nothing, and never exits 2
|
|
239
|
+
- An impeccable older than major 4, or one whose version it cannot read, warns on stderr and stays on
|
|
240
|
+
- `--json` prints the same answer as `{"on": ..., "reasons": [...], "impeccable": ...}`
|
|
241
|
+
|
|
242
|
+
### 3.1. What `workflow.tdd.strict` moves
|
|
243
|
+
|
|
244
|
+
*`workflow.tdd.strict` reorders the first two phases. It drops none of them.*
|
|
245
|
+
|
|
246
|
+
- The default `true` runs test-writer, then task-executor, then task-checker
|
|
247
|
+
- A `false` runs task-executor, then test-writer, then task-checker
|
|
248
|
+
- All three agents run either way
|
|
249
|
+
- Six files read the key: `orchestrator/AGENTS.md`, `skills/implement/SKILL.md`, `agents/test-writer.md`, `agents/task-executor.md`, `agents/task-checker.md`, and `skills/setup/SKILL.md`
|
|
250
|
+
- Relaxed TDD, the name for `false`, means nobody writes the tests first
|
|
251
|
+
- It does not mean the work goes unverified
|
|
252
|
+
- The rule of a checker after every executor does not move
|
|
253
|
+
|
|
254
|
+
Relaxed costs the test-writer part of its independence.
|
|
255
|
+
|
|
256
|
+
- Under `true` it cannot read an implementation
|
|
257
|
+
- None exists when it runs
|
|
258
|
+
- Under `false` the implementation sits in the working tree beside it, and only its instructions keep it from reading that code
|
|
259
|
+
- The two settings do not give equal independence, and `agents/test-writer.md` states the weaker guarantee as the cost of relaxed
|
|
260
|
+
|
|
261
|
+
What the checker gates on moves with the key.
|
|
262
|
+
|
|
263
|
+
- Under `true` the checker holds the task's new tests to failing before the executor and passing after
|
|
264
|
+
- Under `false` the implementation came first
|
|
265
|
+
- Nothing can show those tests failing without it
|
|
266
|
+
- The checker then holds the new tests to existing and passing
|
|
267
|
+
- It holds the full suite to passing
|
|
268
|
+
- The test count to not dropping
|
|
269
|
+
|
|
270
|
+
### 3.2. How the agent asks you a question
|
|
271
|
+
|
|
272
|
+
| Key | Values | Default | What changes |
|
|
273
|
+
| --- | --- | --- | --- |
|
|
274
|
+
| `workflow.grilling.questions` | `tool`, `inline` | `tool` | `tool` asks through the host's question tool, one call per round; `inline` asks in prose |
|
|
275
|
+
|
|
276
|
+
- `tool` falls back to inline prose for any round it cannot hold
|
|
277
|
+
- Two things exceed it: a round of more than four questions, and any question with no discrete options
|
|
278
|
+
|
|
279
|
+
## 4. workflow.maps
|
|
280
|
+
|
|
281
|
+
*Two keys record where a map's nodes live, and what happens to those nodes at the end.*
|
|
282
|
+
|
|
283
|
+
A **map** is a graph of question nodes and work nodes. A **tracker** is the backend that stores those nodes.
|
|
284
|
+
|
|
285
|
+
| Key | Values | Default | What changes |
|
|
286
|
+
| --- | --- | --- | --- |
|
|
287
|
+
| `workflow.maps.tracker` | `github`, `files` | none | the `map`, `implement`, and `archive` skills read the matching backend document and use its operations |
|
|
288
|
+
| `workflow.maps.persist` | `true`, `false` | `false` | `true` moves a cleared map's node files to `acstack/archive/[YYYY-MM-DD]-[name]/nodes/`; `false` deletes `acstack/maps/<name>/` |
|
|
289
|
+
|
|
290
|
+
- An absent `workflow.maps.tracker` means nobody has chosen yet
|
|
291
|
+
- The `map` skill then picks at the moment it first writes a map's nodes down, confirms the choice with the user, and writes the key
|
|
292
|
+
- It recommends `github` when `gh` holds a login for the repository's GitHub remote, and `files` otherwise
|
|
293
|
+
- Anything that resets the `workflow` block to defaults has to keep `workflow.maps` intact
|
|
294
|
+
- The key records where the node records already are
|
|
295
|
+
- Dropping it strands every one of them on a backend no later session looks at
|
|
296
|
+
- `workflow.maps.persist` acts on the files tracker only
|
|
297
|
+
- The GitHub tracker has nothing to dispose of
|
|
298
|
+
- A closed issue is already the archive
|
|
299
|
+
- The default deletes, because keeping the nodes leaves a second copy of every decision and the two copies drift apart
|
|
300
|
+
|
|
301
|
+
## 5. workflow.handoff: launching and acknowledging
|
|
302
|
+
|
|
303
|
+
*Three keys govern the handoff skill, which moves work from one session to another.*
|
|
304
|
+
|
|
305
|
+
| Key | Values | Default | What changes |
|
|
306
|
+
| --- | --- | --- | --- |
|
|
307
|
+
| `workflow.handoff.agent` | a command template | `claude` | the command the `handoff` skill runs to launch a target session when herdr is absent; a template rather than a bare name, so a model flag or a permission-mode flag fits |
|
|
308
|
+
| `workflow.handoff.ack_turns` | a whole number, 0 or more | `5` | how many turns after delivery the sender waits before it reports the target as silent; `--fresh` doubles it |
|
|
309
|
+
| `workflow.handoff.self_invoke` | `true`, `false` | `true` | `false` stops the agent from invoking the `handoff` skill on its own initiative; it then tells the user a handoff looks warranted and asks first |
|
|
310
|
+
|
|
311
|
+
- `workflow.handoff.self_invoke` gets a behavioural check at the skill's first step, rather than a frontmatter flag
|
|
312
|
+
- Frontmatter model-invocation flags are static
|
|
313
|
+
- No project could turn one off for itself
|
|
314
|
+
|
|
315
|
+
## 6. workflow.handoff: the context-pressure ladder
|
|
316
|
+
|
|
317
|
+
*Five keys place the rungs at which `hooks/context-pressure.sh` asks the session to choose continuity or freshness.*
|
|
318
|
+
|
|
319
|
+
How the hook decides to speak:
|
|
320
|
+
|
|
321
|
+
- The hook runs on every stop
|
|
322
|
+
- It reads the last assistant record in the transcript and adds three usage fields: `input_tokens`, `cache_read_input_tokens`, and `cache_creation_input_tokens`
|
|
323
|
+
- That sum is the context the turn sent to the model
|
|
324
|
+
- The hook then finds the highest rung at or below that number
|
|
325
|
+
- It speaks when that rung sits above the rung it last spoke for
|
|
326
|
+
- It records the new rung
|
|
327
|
+
- So a long session gets one nudge per rung, not one per stop
|
|
328
|
+
|
|
329
|
+
### 6.1 The default thresholds, and the gap above them
|
|
330
|
+
|
|
331
|
+
| Key | Values | Default | What changes |
|
|
332
|
+
| --- | --- | --- | --- |
|
|
333
|
+
| `workflow.handoff.nudge_warn` | a whole number of tokens, 0 or more | `200000` | the ladder's first rung, and the wording below the severe mark; a small-context model wants a lower value |
|
|
334
|
+
| `workflow.handoff.nudge_severe` | a whole number of tokens, 0 or more | `500000` | the ladder's second rung, and the mark at or above which the wording turns urgent |
|
|
335
|
+
| `workflow.handoff.nudge_step` | a whole number of tokens, 0 or more | `100000` | how far apart the rungs sit above the last one, so a session that keeps growing keeps hearing from the hook |
|
|
336
|
+
|
|
337
|
+
Three guards protect the ladder from a value that would break it.
|
|
338
|
+
|
|
339
|
+
- The hook raises a `nudge_warn` below 1 to 1
|
|
340
|
+
- A rung of 0 would block every single turn
|
|
341
|
+
- It raises `nudge_severe` to `nudge_warn` when the file states a lower one
|
|
342
|
+
- A severe mark below the warn mark could never fire
|
|
343
|
+
- It treats a `nudge_step` below 1 as an instruction to stop the ladder at its top rung
|
|
344
|
+
|
|
345
|
+
What the nudge asks the agent to choose:
|
|
346
|
+
|
|
347
|
+
- Continuity means useful working context stays in the same session
|
|
348
|
+
- A large window can rely on billion-context
|
|
349
|
+
- ACP compression is an in-place host option, not something ACStack calls itself
|
|
350
|
+
- Freshness means a fresh context window or independent read would help more
|
|
351
|
+
- That route is `/acstack:handoff`
|
|
352
|
+
- Severe wording makes the same checkpoint urgent
|
|
353
|
+
- It never halts the run
|
|
354
|
+
- Both tiers close with the same clause
|
|
355
|
+
- The agent decides for itself
|
|
356
|
+
- It asks the user only when it genuinely cannot tell
|
|
357
|
+
- It hands over without asking when the user already agreed to a handoff earlier in the session
|
|
358
|
+
|
|
359
|
+
### 6.2 An explicit ladder
|
|
360
|
+
|
|
361
|
+
| Key | Values | Default | What changes |
|
|
362
|
+
| --- | --- | --- | --- |
|
|
363
|
+
| `workflow.handoff.context_thresholds` | a list of token counts or percentage strings | none | replaces `nudge_warn` and `nudge_severe` as the ladder's rungs, as in `[150000, 300000]` or `["40%", "70%"]` |
|
|
364
|
+
|
|
365
|
+
- Two YAML shapes work: flow style on one line, `[150000, 300000]`, and block style with one `- item` per line
|
|
366
|
+
- The hook sorts the rungs and drops duplicates
|
|
367
|
+
- It drops any entry it cannot read as a number or a percentage, and any entry below 1
|
|
368
|
+
- It falls back to the `nudge_warn` and `nudge_severe` rungs with no usable entry left
|
|
369
|
+
|
|
370
|
+
All three of the other keys still act under an explicit ladder.
|
|
371
|
+
|
|
372
|
+
- `nudge_step` extends the ladder past the last listed rung, exactly as it does by default
|
|
373
|
+
- `nudge_severe` places no rung any more, and only picks the wording
|
|
374
|
+
- A nudge reads as severe once the rung that fired sits at or above it
|
|
375
|
+
- `nudge_warn` places no rung either
|
|
376
|
+
- It still moves that wording threshold
|
|
377
|
+
- The hook raises `nudge_severe` to meet a higher `nudge_warn` before it builds the ladder
|
|
378
|
+
|
|
379
|
+
### 6.3 What a percentage is a percentage of
|
|
380
|
+
|
|
381
|
+
| Key | Values | Default | What changes |
|
|
382
|
+
| --- | --- | --- | --- |
|
|
383
|
+
| `workflow.handoff.context_window` | a whole number of tokens, 1 or more | inferred from the model id | the number a percentage rung resolves against, as in `"40%"` of `200000` |
|
|
384
|
+
|
|
385
|
+
- A positive `context_window` wins outright
|
|
386
|
+
- The hook infers the window from the model id with no value, or one of 0 or less
|
|
387
|
+
- That id comes from the same transcript record as the token count
|
|
388
|
+
|
|
389
|
+
Three rules decide the window, in order.
|
|
390
|
+
|
|
391
|
+
| The model id | The window |
|
|
392
|
+
| --- | --- |
|
|
393
|
+
| carries the `[1m]` marker | 1,000,000 |
|
|
394
|
+
| names the haiku line, or a 4.x family such as `claude-opus-4-8`, `claude-haiku-4-5-...` or `claude-sonnet-4-5` | 200,000 |
|
|
395
|
+
| anything else, including the 5.x families and a record with no model id at all | 1,000,000 |
|
|
396
|
+
|
|
397
|
+
- The `[1m]` marker outranks the second rule
|
|
398
|
+
- A million-token model says so in its own id
|
|
399
|
+
- `claude-sonnet-4-5[1m]` resolves to 1,000,000 rather than 200,000
|
|
400
|
+
|
|
401
|
+
### 6.4 Why only the session's own stop
|
|
402
|
+
|
|
403
|
+
*The hook nudges the session that could act on it. It nudges nobody else.*
|
|
404
|
+
|
|
405
|
+
- `hooks/hooks.json` registers it for two events only
|
|
406
|
+
- A `Stop` event fires when the session's own turn finishes
|
|
407
|
+
- `SessionStart` fires when a session opens
|
|
408
|
+
- A `SubagentStop` payload, should one ever arrive, exits silently and creates no state
|
|
409
|
+
- Neither a subagent nor an in-process teammate can hand the user's work over
|
|
410
|
+
- A nudge there would only spend a turn
|
|
411
|
+
- The measurement follows the same rule
|
|
412
|
+
- The hook skips every transcript record marked `isSidechain`
|
|
413
|
+
- A subagent's tokens are not this session's context
|
|
414
|
+
|
|
415
|
+
### 6.5 The ladder is per session, and a compaction resets it
|
|
416
|
+
|
|
417
|
+
*Two files per session hold the state, and a compaction deletes both.*
|
|
418
|
+
|
|
419
|
+
- The hook keeps both files under `$ACSTACK_CONTEXT_PRESSURE_DIR`
|
|
420
|
+
- That defaults to a `acstack-context-pressure` directory under `TMPDIR`, or under `/tmp`
|
|
421
|
+
- The `.last` file holds the rung the hook last spoke for
|
|
422
|
+
- The `.quiet` file is a marker
|
|
423
|
+
- The `handoff` and `compact-and-continue` skills leave it behind when they finish
|
|
424
|
+
- The hook stays silent for the rest of the session while it exists
|
|
425
|
+
- A compaction deletes both files
|
|
426
|
+
- The ladder starts again at its first rung
|
|
427
|
+
- A compaction throws the session's context away and rebuilds it much smaller
|
|
428
|
+
- The recorded rung then describes context that no longer exists
|
|
429
|
+
- The quiet marker records a handover the fresh context knows nothing about
|
|
430
|
+
|
|
431
|
+
Two limits are worth knowing.
|
|
432
|
+
|
|
433
|
+
- The hook reads these five keys with its own small YAML parser
|
|
434
|
+
- pyyaml may not exist on the machine
|
|
435
|
+
- That parser hands each value to Python's `int()` and accepts whatever `int()` accepts
|
|
436
|
+
- A `_` digit separator works
|
|
437
|
+
- `200_000` reads as 200,000
|
|
438
|
+
- Exponent and float notation do not
|
|
439
|
+
- `2e5` and `2.0` fall back to the default
|
|
440
|
+
- The hook opens `acstack/project.yaml` relative to the working directory
|
|
441
|
+
- A session running outside the project root therefore gets the defaults, whatever the file says
|
|
442
|
+
|
|
443
|
+
## 7. commands, directories, test_markers and venv
|
|
444
|
+
|
|
445
|
+
*What every agent runs, and where every agent looks.*
|
|
446
|
+
|
|
447
|
+
| Key | Values | Default | What changes |
|
|
448
|
+
| --- | --- | --- | --- |
|
|
449
|
+
| `commands.test` | a shell command, or `null` | from the profile | the test-writer, task-executor, and task-checker run it; the task-checker fails a task whose tests do not pass |
|
|
450
|
+
| `commands.test_collect` | a shell command, or `null` | from the profile | the task-checker counts tests with it and compares the count against `.test-baseline`; `null` skips the baseline check |
|
|
451
|
+
| `commands.build` | a shell command, or `null` | from the profile | the task-checker verifies the build with it |
|
|
452
|
+
| `commands.lint` | a shell command, or `null` | from the profile | the pipeline lints with it, usually with a fix flag |
|
|
453
|
+
| `commands.typecheck` | a shell command, or `null` | from the profile | the pipeline type-checks with it |
|
|
454
|
+
| `commands.format` | a shell command, or `null` | from the profile | the pipeline runs it over the files the work touched, immediately before the task-checker; `null` means no format step |
|
|
455
|
+
| `directories.source` | a path | `src/` | where `/acstack:setup` explores to propose the domain map, and where the pipeline writes source |
|
|
456
|
+
| `directories.tests` | a path | `tests/` | where the test-writer writes tests, and the one tree the task-executor may not touch |
|
|
457
|
+
| `test_markers.exclude` | comma-separated markers, or `null` | `null` for Node, `slow,integration` for Python | which markers a default test run leaves out, as in `pytest -m` |
|
|
458
|
+
| `venv.activate` | a shell command, or `null` | `null` for Node, `source .venv/bin/activate` for Python | every agent prefixes each command with it when the file states one |
|
|
459
|
+
|
|
460
|
+
- One rule places the format step under both TDD settings
|
|
461
|
+
- It runs immediately before the task-checker
|
|
462
|
+
- Under relaxed TDD it falls after the test-writer and formats the new tests too
|
|
463
|
+
- A non-zero exit from `commands.format` gets reported, and never counts as a failed implementation
|
|
464
|
+
- Formatting is not correctness
|
|
465
|
+
- The task-checker runs afterwards, and its own gate decides whether the work passes
|
|
466
|
+
- `acstack config show` lists a `commands` entry only when its value is a non-empty string
|
|
467
|
+
- A `null`, a number or a nested block does not name a command anyone could run
|
|
468
|
+
- The listing leaves it out
|
|
469
|
+
|
|
470
|
+
## 8. frontend
|
|
471
|
+
|
|
472
|
+
*The block's presence is itself a setting: with no `frontend` block, this project drives no browser.*
|
|
473
|
+
|
|
474
|
+
| Key | Values | Default | What changes |
|
|
475
|
+
| --- | --- | --- | --- |
|
|
476
|
+
| `frontend` | a block, or `null` | absent | a block makes the project a frontend project: the frontend-verifier becomes eligible, and `acstack config check` starts asking for the three browser axes and the frontend files |
|
|
477
|
+
| `frontend.directory` | a path | `frontend/` | where the frontend lives |
|
|
478
|
+
| `frontend.dev_server_url` | a URL | `http://localhost:3000` | the address the frontend-verifier opens |
|
|
479
|
+
| `frontend.dev_server_check` | a shell command | a `curl` of the URL | how the frontend-verifier decides the dev server is up |
|
|
480
|
+
| `frontend.helpers_dir` | a path | `<frontend.directory>/tests/helpers/` | where `VERIFICATION-KNOWLEDGE.md` lives; the `verification-knowledge` row of the check fails when the file is missing, and when the key states nothing |
|
|
481
|
+
| `frontend.commands.dev` | a shell command | none | how the frontend-verifier starts the dev server; without it the verifier cannot start one itself |
|
|
482
|
+
| `frontend.commands.build` | a shell command | from the profile | the frontend build the pipeline runs |
|
|
483
|
+
| `frontend.commands.lint` | a shell command | from the profile | the frontend lint the pipeline runs |
|
|
484
|
+
| `frontend.commands.test` | a shell command | from the profile | the frontend tests the pipeline runs |
|
|
485
|
+
|
|
486
|
+
- An explicit `frontend: null` reads the same as no block at all
|
|
487
|
+
- The CLI counts a frontend only when the key holds something other than `null`
|
|
488
|
+
|
|
489
|
+
## 9. frontend.browser and the CDP port
|
|
490
|
+
|
|
491
|
+
*Three keys state what this project would like. The machine states what it can actually do.*
|
|
492
|
+
|
|
493
|
+
The Chrome DevTools Protocol (CDP) is the wire protocol the verifier drives a browser over. These three keys state a preference, and the `host.browser.*` axes in the global config say whether the machine can honour it. [dev-setups.md](dev-setups.md) documents those axes.
|
|
494
|
+
|
|
495
|
+
| Key | Values | Default | What changes |
|
|
496
|
+
| --- | --- | --- | --- |
|
|
497
|
+
| `frontend.browser.mode` | `remote`, `headless`, `local` | none | the mode `acstack config browser-mode` resolves for the frontend-verifier, weighed against the three host axes; it also picks the `cdp_port` default |
|
|
498
|
+
| `frontend.browser.fallback` | `none`, `remote`, `headless`, `local` | none | only `none` acts: it forbids another mode from standing in when the host does not declare the preferred one |
|
|
499
|
+
| `frontend.browser.cdp_port` | a whole number from 1 to 65535 | `19988` under `mode: remote`, `9555` otherwise | the port the frontend-verifier dials, and the port the `agent-browser-json` row of the check requires `agent-browser.json` to name |
|
|
500
|
+
|
|
501
|
+
The two defaults come from two different browsers.
|
|
502
|
+
|
|
503
|
+
- The mode `remote` means the Playwriter bridge, which hardcodes port 19988 and offers no way to change it
|
|
504
|
+
- Every other mode means a browser this machine launched itself, with the usual debug port 9555
|
|
505
|
+
- The default is 9555 with no `mode` at all
|
|
506
|
+
|
|
507
|
+
The port default applies wherever the CLI needs a number.
|
|
508
|
+
|
|
509
|
+
- `acstack config check` holds a project that states no `cdp_port` to the default its mode implies
|
|
510
|
+
- It fails when `agent-browser.json` names a different port
|
|
511
|
+
- `acstack config show` behaves the other way, printing only what the project states
|
|
512
|
+
- "What did the project say" and "which port do we dial" are different questions
|
|
513
|
+
|
|
514
|
+
Only the literal `none` gives `frontend.browser.fallback` any effect.
|
|
515
|
+
|
|
516
|
+
- Every other value, `headless` included, leaves the host's own order alone: remote first, then headless, then local
|
|
517
|
+
- A project that wanted a particular mode would have named it as its `mode`
|
|
518
|
+
- A second mode name here changes nothing
|
|
519
|
+
- The reader ignores any word other than `none`, and `acstack config set` still holds the key to the four names above
|
|
520
|
+
- A typo would read as a fallback the project never asked for
|
|
521
|
+
- The command refuses it rather than storing it
|
|
522
|
+
|
|
523
|
+
## 10. What is not a project.yaml key
|
|
524
|
+
|
|
525
|
+
*Two sets of settings look like they belong here. Neither set does.*
|
|
526
|
+
|
|
527
|
+
- The `outputStyle` setting is not a `project.yaml` key
|
|
528
|
+
- Claude Code owns it
|
|
529
|
+
- It lives in three settings files
|
|
530
|
+
- `.claude/settings.local.json` wins over `.claude/settings.json`, which in turn wins over `~/.claude/settings.json`
|
|
531
|
+
- `acstack config check` reads all three and reports which file selects which style
|
|
532
|
+
- No ACStack command writes it, and `acstack config set` refuses it as a key neither schema knows
|
|
533
|
+
- `/acstack:setup` offers to write it for you, and asks first
|
|
534
|
+
- The `host.*` keys are not `project.yaml` keys either
|
|
535
|
+
- They describe the machine rather than the project
|
|
536
|
+
- The same repository opens on several machines with different setups
|
|
537
|
+
- They live in the global config at `~/.config/acstack/config.json`
|
|
538
|
+
- `acstack config set` is what writes them
|
|
539
|
+
- [dev-setups.md](dev-setups.md) documents all nine axes
|
|
540
|
+
|
|
541
|
+
## 11. Every default in one block
|
|
542
|
+
|
|
543
|
+
*What ACStack does with a `project.yaml` that states none of these keys.*
|
|
544
|
+
|
|
545
|
+
```yaml
|
|
546
|
+
workflow:
|
|
547
|
+
spec_sync: true
|
|
548
|
+
grilling:
|
|
549
|
+
questions: tool
|
|
550
|
+
tdd:
|
|
551
|
+
strict: true
|
|
552
|
+
orchestrator_strict: true
|
|
553
|
+
# /acstack:setup writes true for a project with a frontend.
|
|
554
|
+
# An absent key reads as false. Section 3 says why.
|
|
555
|
+
frontend_verification: true
|
|
556
|
+
design_review: false
|
|
557
|
+
maps:
|
|
558
|
+
# tracker has no default: the map skill picks and writes it
|
|
559
|
+
persist: false
|
|
560
|
+
handoff:
|
|
561
|
+
agent: "claude"
|
|
562
|
+
ack_turns: 5
|
|
563
|
+
self_invoke: true
|
|
564
|
+
nudge_warn: 200000
|
|
565
|
+
nudge_severe: 500000
|
|
566
|
+
nudge_step: 100000
|
|
567
|
+
# context_thresholds and context_window have no default
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
- The `commands`, `directories`, `test_markers`, `venv`, and `frontend` blocks have no defaults of their own
|
|
571
|
+
- `/acstack:setup` copies them from the profile it detects
|
|
572
|
+
- Section 7 gives the values each profile proposes
|