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.
Files changed (143) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +389 -2
  4. package/THIRD_PARTY_NOTICES +14 -0
  5. package/TROUBLESHOOTING.md +225 -0
  6. package/agents/codex/frontend-verifier.toml +352 -0
  7. package/agents/codex/task-checker.toml +345 -0
  8. package/agents/codex/task-executor.toml +157 -0
  9. package/agents/codex/test-writer.toml +149 -0
  10. package/agents/frontend-verifier.md +351 -0
  11. package/agents/task-checker.md +344 -0
  12. package/agents/task-executor.md +156 -0
  13. package/agents/test-writer.md +148 -0
  14. package/assets/playwriter-bridge/build-launcher.ps1 +32 -0
  15. package/assets/playwriter-bridge/doctor.ps1 +450 -0
  16. package/assets/playwriter-bridge/launcher-src.cs +131 -0
  17. package/assets/playwriter-bridge/opener.js +330 -0
  18. package/assets/playwriter-bridge/opener.ps1 +63 -0
  19. package/assets/playwriter-bridge/register-tasks.ps1 +328 -0
  20. package/assets/playwriter-bridge/relay.ps1 +50 -0
  21. package/assets/playwriter-bridge/stop.ps1 +125 -0
  22. package/assets/playwriter-bridge/sync.ps1 +271 -0
  23. package/assets/playwriter-bridge/tunnel.ps1 +325 -0
  24. package/assets/playwriter-bridge/vm-free-port.sh +166 -0
  25. package/assets/terminal-workspace/client-keybindings.md +87 -0
  26. package/assets/terminal-workspace/config.example.yaml +329 -0
  27. package/assets/terminal-workspace/setup.sh +4317 -0
  28. package/cli/README.md +47 -0
  29. package/cli/bin/acstack.js +3 -0
  30. package/cli/dist/index.js +25984 -0
  31. package/cli/dist/index.js.map +7 -0
  32. package/cli/package.json +59 -0
  33. package/docs/adr/0001-ste-writing-standard.md +16 -0
  34. package/docs/adr/0002-version-gate-not-auto-bump.md +35 -0
  35. package/docs/adr/0003-handoff-ack-command.md +19 -0
  36. package/docs/adr/0004-dev-setup-per-host.md +13 -0
  37. package/docs/adr/0005-two-orchestrators-per-host.md +25 -0
  38. package/docs/adr/0006-document-opener-service.md +89 -0
  39. package/docs/adr/0007-lead-check-reads-the-transcript.md +87 -0
  40. package/docs/adr/0008-cli-audits-skill-interviews.md +37 -0
  41. package/docs/adr/0009-verify-the-write-not-the-writer.md +51 -0
  42. package/docs/adr/0010-promote-dev-to-main-by-fast-forward.md +115 -0
  43. package/docs/adr/0011-node-header-is-the-edge-encoding.md +20 -0
  44. package/docs/adr/0012-node-kind-is-a-closed-set-of-five.md +31 -0
  45. package/docs/adr/0013-the-diagram-renderer-is-pure.md +23 -0
  46. package/docs/adr/0014-reading-a-node-hides-its-generated-diagram.md +29 -0
  47. package/docs/adr/0015-a-node-id-links-to-its-issue-through-an-anchor.md +37 -0
  48. package/docs/adr/0016-the-map-root-carries-no-status-of-its-own.md +21 -0
  49. package/docs/adr/0017-call-impeccable-copy-only-its-questions.md +13 -0
  50. package/docs/adr/index.md +17 -0
  51. package/docs/config-reference.md +572 -0
  52. package/docs/dev-setups.md +164 -0
  53. package/docs/migrate-0.8.md +28 -0
  54. package/docs/playwriter-bridge-windows.md +15 -0
  55. package/docs/terminal-workspace-keys.md +137 -0
  56. package/docs/terminal-workspace.md +1774 -0
  57. package/docs/workflows.md +309 -0
  58. package/hooks/ac-writing-style-reminder.sh +86 -0
  59. package/hooks/context-pressure.sh +583 -0
  60. package/hooks/hooks.json +50 -0
  61. package/hooks/session-start-handoff.sh +165 -0
  62. package/hooks/session-start.sh +214 -0
  63. package/orchestrator/AGENTS.md +532 -0
  64. package/output-styles/ac-writing-style.md +286 -0
  65. package/package.json +50 -4
  66. package/pi/agents/frontend-verifier.md +357 -0
  67. package/pi/agents/task-checker.md +348 -0
  68. package/pi/agents/task-executor.md +160 -0
  69. package/pi/agents/test-writer.md +154 -0
  70. package/pi/extension.ts +723 -0
  71. package/profiles/fullstack-python.yaml +29 -0
  72. package/profiles/node-typescript.yaml +23 -0
  73. package/profiles/python.yaml +24 -0
  74. package/skills/archive/SKILL.md +160 -0
  75. package/skills/bootstrap/SKILL.md +109 -0
  76. package/skills/bridge/HANDOFF.md +93 -0
  77. package/skills/bridge/SKILL-VM.md +292 -0
  78. package/skills/bridge/SKILL-WINDOWS.md +526 -0
  79. package/skills/bridge/SKILL.md +121 -0
  80. package/skills/browser-verify/SKILL.md +229 -0
  81. package/skills/code-review/SKILL.md +44 -0
  82. package/skills/commit/SKILL.md +106 -0
  83. package/skills/compact-and-continue/SKILL.md +241 -0
  84. package/skills/explore/SKILL.md +149 -0
  85. package/skills/grilling/SKILL.md +134 -0
  86. package/skills/handoff/SKILL.md +250 -0
  87. package/skills/handoff/launch-herdr.md +197 -0
  88. package/skills/handoff/launch-without-herdr.md +30 -0
  89. package/skills/handoff/lead-check.md +59 -0
  90. package/skills/handoff/outcomes.md +162 -0
  91. package/skills/handoff/running-agent.md +50 -0
  92. package/skills/handoff/watch.md +115 -0
  93. package/skills/host/SKILL.md +247 -0
  94. package/skills/host/detect-host.sh +297 -0
  95. package/skills/host/herdr.md +79 -0
  96. package/skills/host/install-herdr-block.sh +467 -0
  97. package/skills/host/install-orca.sh +512 -0
  98. package/skills/host/notes.md +97 -0
  99. package/skills/host/orca.md +160 -0
  100. package/skills/host/questions.md +163 -0
  101. package/skills/host/report.md +42 -0
  102. package/skills/implement/SKILL.md +230 -0
  103. package/skills/implement/decision-log.md +62 -0
  104. package/skills/implement/design-pass.md +32 -0
  105. package/skills/implement/relaxed-tdd.md +28 -0
  106. package/skills/map/SKILL.md +250 -0
  107. package/skills/map/frontend.md +227 -0
  108. package/skills/map/trackers/files.md +74 -0
  109. package/skills/map/trackers/github.md +180 -0
  110. package/skills/map/visuals.md +387 -0
  111. package/skills/new-worktree/SKILL.md +155 -0
  112. package/skills/new-worktree/detect-orchestrator.sh +328 -0
  113. package/skills/new-worktree/detect-orchestrator.test.sh +894 -0
  114. package/skills/new-worktree/herdr.md +84 -0
  115. package/skills/new-worktree/orca.md +101 -0
  116. package/skills/pre-commit-review/SKILL.md +242 -0
  117. package/skills/pre-commit-review/auto-fix.md +19 -0
  118. package/skills/pre-commit-review/interactive.md +35 -0
  119. package/skills/quick-fix/SKILL.md +125 -0
  120. package/skills/record-context/SKILL.md +106 -0
  121. package/skills/setup/SKILL.md +250 -0
  122. package/skills/setup/browser.md +123 -0
  123. package/skills/setup/design-review.md +97 -0
  124. package/skills/setup/first-run.md +114 -0
  125. package/skills/setup/report.md +68 -0
  126. package/skills/setup/row-fixes.md +196 -0
  127. package/skills/setup/terminal-workspace.md +153 -0
  128. package/skills/sync/SKILL.md +171 -0
  129. package/skills/teardown-worktree/SKILL.md +245 -0
  130. package/skills/teardown-worktree/detector.md +39 -0
  131. package/skills/teardown-worktree/herdr.md +113 -0
  132. package/skills/teardown-worktree/orca.md +61 -0
  133. package/skills/terminal-workspace/SKILL.md +249 -0
  134. package/skills/terminal-workspace/components.md +36 -0
  135. package/skills/terminal-workspace/config-choices.md +69 -0
  136. package/skills/terminal-workspace/emulator-keys.md +48 -0
  137. package/skills/terminal-workspace/headless-clipboard.md +175 -0
  138. package/skills/terminal-workspace/turn-off-and-update.md +104 -0
  139. package/skills/test-conventions/SKILL.md +59 -0
  140. package/skills/visual-docs/SKILL.md +246 -0
  141. package/skills/writing/SKILL.md +231 -0
  142. package/skills/writing/examples.md +153 -0
  143. package/skills/writing/vocabulary.md +96 -0
@@ -0,0 +1,29 @@
1
+ # Fullstack Python (backend) + Node/TS (frontend) profile for acstack
2
+ # Used by /acstack:setup to generate acstack/project.yaml
3
+
4
+ commands:
5
+ test: "pytest tests/ --tb=short -x"
6
+ test_collect: "pytest tests/ --co -q 2>/dev/null | tail -1 | grep -oP '\\d+'"
7
+ build: null
8
+ lint: "ruff check --fix"
9
+ typecheck: null
10
+ format: "ruff format"
11
+
12
+ directories:
13
+ source: "src/"
14
+ tests: "tests/"
15
+
16
+ test_markers:
17
+ exclude: "slow,integration"
18
+
19
+ venv:
20
+ activate: "source .venv/bin/activate"
21
+
22
+ frontend:
23
+ directory: "frontend/"
24
+ dev_server_url: "http://localhost:3000"
25
+ dev_server_check: "curl -s -o /dev/null -w '%{http_code}' http://localhost:3000"
26
+ commands:
27
+ build: "npx tsc --noEmit"
28
+ lint: "npm run lint -- --fix"
29
+ test: "npm test"
@@ -0,0 +1,23 @@
1
+ # Node/TypeScript project profile for acstack
2
+ # Used by /acstack:setup to generate acstack/project.yaml
3
+
4
+ commands:
5
+ test: "npm test"
6
+ test_collect: null
7
+ build: "npm run build"
8
+ lint: "npm run lint -- --fix"
9
+ typecheck: "npx tsc --noEmit"
10
+ format: "npx prettier --write ."
11
+
12
+ directories:
13
+ source: "src/"
14
+ tests: "tests/"
15
+
16
+ test_markers:
17
+ exclude: null
18
+
19
+ venv: null
20
+
21
+ frontend:
22
+ dev_server_url: "http://localhost:3000"
23
+ dev_server_check: "curl -s -o /dev/null -w '%{http_code}' http://localhost:3000"
@@ -0,0 +1,24 @@
1
+ # Python project profile for acstack
2
+ # Used by /acstack:setup to generate acstack/project.yaml
3
+
4
+ commands:
5
+ test: "pytest tests/ --tb=short -x"
6
+ test_collect: "pytest tests/ --co -q 2>/dev/null | tail -1 | grep -oP '\\d+'"
7
+ build: null
8
+ lint: "ruff check --fix"
9
+ typecheck: "mypy ."
10
+ format: "ruff format"
11
+
12
+ directories:
13
+ source: "src/"
14
+ tests: "tests/"
15
+
16
+ test_markers:
17
+ # Markers to exclude from default test runs (comma-separated for pytest -m)
18
+ exclude: "slow,integration"
19
+
20
+ venv:
21
+ # If set, commands are prefixed with activation
22
+ activate: "source .venv/bin/activate"
23
+
24
+ frontend: null
@@ -0,0 +1,160 @@
1
+ ---
2
+ name: archive
3
+ description: Close out a cleared map – one whose frontier is empty with no fog and no claims left – by verifying the durable residue, meaning what the effort leaves behind in living specs, ADRs and the glossary, was extracted, then closing the root and disposing of the nodes per workflow.maps.persist. Invoke when a map has just been cleared, when the last node on a map resolves, or when the user asks to archive or close out a map. Refuses and asks first if anything is still open. Also archives legacy acstack/changes/ directories from the pre-map workflow.
4
+ argument-hint: "[map or legacy change name]"
5
+ ---
6
+
7
+ ## User input
8
+
9
+ ```text
10
+ $ARGUMENTS
11
+ ```
12
+
13
+ You **MUST** consider the user input before proceeding (if not empty).
14
+
15
+ ## Purpose
16
+
17
+ A map is scaffolding. It exists to clear fog – the questions nobody could
18
+ state precisely when the effort started.
19
+
20
+ Once the fog clears, the answers belong in living specs, ADRs and the
21
+ glossary, not in a second copy that drifts. That is the residue – what
22
+ survives the map.
23
+
24
+ Archive verifies the residue reached those three places. It then closes the
25
+ map's root node and disposes of the rest.
26
+
27
+ ## Step 1: Locate the map
28
+
29
+ If `$ARGUMENTS` names a map, use it. Otherwise list maps on the configured
30
+ tracker (`workflow.maps.tracker`): `ls acstack/maps/` on the files backend,
31
+ `gh label list --search "map:" --json name` on GitHub.
32
+
33
+ If exactly one, use it. If several, ask. If `$ARGUMENTS` names a legacy
34
+ `acstack/changes/` directory instead, jump to Legacy below.
35
+
36
+ ## Step 2: Verify no open work remains
37
+
38
+ A map counts as cleared when nothing remains to work – no open nodes, no
39
+ fog, no claims. Check all three on the files backend:
40
+
41
+ ```bash
42
+ ~/.claude/acstack/bin/acstack node list --map <name> --status open --json
43
+ ~/.claude/acstack/bin/acstack node list --map <name> --status fog --json
44
+ ~/.claude/acstack/bin/acstack node list --map <name> --status claimed --json
45
+ ```
46
+
47
+ On GitHub, compose the same checks per `trackers/github.md` in the map skill.
48
+ `gh issue list --label "map:<name>" --state open` must come back empty, or
49
+ hold the issue labelled `root-node` alone. Any other open issue is open, fog
50
+ or claimed, and all three fail the gate.
51
+
52
+ Never run the files commands against a GitHub-tracked map. They come back
53
+ empty because the directory does not exist, so the gate would pass on no
54
+ evidence.
55
+
56
+ All three must come back empty, apart from the root.
57
+
58
+ The root is the map's one node with no `answers`, and on GitHub it carries the
59
+ `root-node` label. It stays open until Step 4 closes it. Ignore it in all
60
+ three lists – a list holding the root alone still passes the gate.
61
+
62
+ Count open nodes directly rather than through the frontier query, because the
63
+ frontier only lists nodes with no unresolved blockers. Two nodes that block
64
+ each other show an empty frontier while both questions stay open. An empty
65
+ frontier is therefore no proof of a cleared map.
66
+
67
+ If anything remains, WARN with what it is and ask for confirmation –
68
+ archiving an uncleared map throws away open questions.
69
+
70
+ ## Step 3: Extract the residue
71
+
72
+ Walk the resolved nodes – read them in order, root first, then each node that
73
+ hangs off it. On the files backend, run `acstack node walk --map <name> --full`.
74
+ On GitHub, compose the walk per `trackers/github.md`.
75
+
76
+ Check each resolution left what it should have:
77
+
78
+ 1. **Living specs** – behaviour the effort built should already be in
79
+ `acstack/specs/` via commit-time sync. Spot-check the affected domains.
80
+ Fix gaps on sight per the Spec Correction Protocol.
81
+
82
+ 2. **ADRs and glossary** – invoke `record-context` for any resolved decision
83
+ that meets the bar and that nobody recorded. This is the last chance.
84
+
85
+ 3. **Out-of-scope nodes** – report them. A scope boundary is worth the user
86
+ hearing once more before the map disappears.
87
+
88
+ 4. **The decision log** – read `<main checkout>/acstack/.decisions/<name>.tsv`
89
+ if it exists. The main checkout is `dirname` of `git rev-parse
90
+ --path-format=absolute --git-common-dir`.
91
+
92
+ afk runs append one row per decision an agent made alone. Hand each
93
+ lasting decision to `record-context`. A later row supersedes an earlier
94
+ one it contradicts.
95
+
96
+ ## Step 4: Close the root
97
+
98
+ The root carries no status of its own – it stays open while any node below it
99
+ still needs work. Step 2 proved nothing does. Resolve the root now.
100
+
101
+ ```bash
102
+ # files backend
103
+ ~/.claude/acstack/bin/acstack node update <root id> --map <name> --status resolved
104
+
105
+ # github backend
106
+ gh issue close <root issue number> --reason completed
107
+ ```
108
+
109
+ A root the map already resolved or closed stays that way. Never reopen it.
110
+
111
+ ## Step 5: Dispose of the nodes
112
+
113
+ **Confirm first when you invoked this yourself.** A user who typed
114
+ `/acstack:archive` has already asked for disposal – proceed.
115
+
116
+ If you reached this skill on your own after a map cleared, name what you
117
+ will delete or move. Then wait for the user's go-ahead. Deletion is
118
+ irreversible and they never asked for it.
119
+
120
+ `workflow.maps.persist` in `acstack/project.yaml` decides (default `false`):
121
+
122
+ | Value | Action |
123
+ | ----------------- | ------------------------------------------------------------ |
124
+ | `false` (default) | delete `acstack/maps/<name>/` |
125
+ | `true` | move the nodes to `acstack/archive/[YYYY-MM-DD]-[name]/nodes/` |
126
+
127
+ Default is delete because keeping the nodes leaves a second copy of every
128
+ decision, and the two drift. On the GitHub tracker there is nothing to
129
+ dispose – closed issues are already the archive.
130
+
131
+ Also dispose of `acstack/handoffs/<name>/` if it exists. Consumed handoffs
132
+ hold conversation content and should not outlive the map they served.
133
+
134
+ Delete the map's decision log, `<main checkout>/acstack/.decisions/<name>.tsv`,
135
+ on either tracker. Step 3 already extracted what it held. Leave the directory
136
+ and its `.gitignore` for the next map.
137
+
138
+ ## Step 6: Report
139
+
140
+ - Nodes resolved / out-of-scope counts
141
+ - Residue: domains spot-checked, ADRs and glossary entries written
142
+ - Disposal: deleted, or archive path
143
+ - Reminder: commit with `/acstack:commit`
144
+
145
+ ## Legacy: archiving a `acstack/changes/` directory
146
+
147
+ The pre-map workflow created changes that hold proposal.md, design.md and
148
+ tasks.md. They still archive the old way, so an upgrade never strands work:
149
+
150
+ 1. Read the change's artifacts. Derive the affected domains from the file
151
+ paths in `tasks.md` via `acstack/domain-map.yaml`.
152
+
153
+ 2. For each domain, merge what the feature ADDED, MODIFIED or REMOVED into
154
+ `acstack/specs/[domain]/spec.md`.
155
+
156
+ 3. Write a `delta.md` into the change directory.
157
+
158
+ Then run the CLI's `acstack archive <name>`. It moves the artifacts,
159
+ delta included, to `acstack/changes/archive/[YYYY-MM-DD]-[change-name]/`.
160
+ It also removes the change from `acstack/changes/`.
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: bootstrap
3
+ description: Multi-pass AI crawl to generate initial living specs from the existing codebase. Use when acstack/specs/ domains need their initial spec.md files bootstrapped from code.
4
+ argument-hint: "[domain-names (optional)]"
5
+ disable-model-invocation: true
6
+ ---
7
+
8
+ ## User input
9
+
10
+ ```text
11
+ $ARGUMENTS
12
+ ```
13
+
14
+ Consider the user input before you proceed, if it is not empty.
15
+
16
+ ## Purpose
17
+
18
+ This skill bootstraps cumulative living specs in `acstack/specs/`. It crawls the existing codebase with multiple independent AI passes. It then merges the passes and validates the results.
19
+
20
+ **IMPORTANT**: This is a one-time bootstrapping operation. Only run when `acstack/specs/` domains have no `spec.md` files yet.
21
+
22
+ ## Your role: orchestrator only
23
+
24
+ **You do NOT read code, explore the codebase, or write specs yourself.**
25
+
26
+ Your job is to:
27
+
28
+ 1. Run the pre-flight check (read domain-map.yaml)
29
+ 2. Launch subagents with the prompts below
30
+ 3. Pass outputs from one phase to the next
31
+ 4. Present summaries to the user for review
32
+ 5. Coordinate the phases sequentially
33
+
34
+ ## Pre-flight check
35
+
36
+ 1. Read `acstack/domain-map.yaml` to get the list of domains and their file path mappings.
37
+ 2. If `$ARGUMENTS` specifies domain names, filter to only those domains.
38
+ 3. Check if any `acstack/specs/*/spec.md` files already exist:
39
+ - If specs exist, warn the user. Ask for confirmation before overwriting.
40
+ - If no specs exist, proceed automatically.
41
+
42
+ ## Phase 1: independent exploration (passes 1-3)
43
+
44
+ Launch **3 parallel `Explore` subagents** at once. Each gets a different exploration strategy:
45
+
46
+ ### Pass 1 – entry points strategy
47
+
48
+ Explore from entry points (main files, route handlers, API endpoints, page components) to discover functionality domain by domain.
49
+
50
+ ### Pass 2 – data models strategy
51
+
52
+ Explore from data models outward (database models, schemas, interfaces) to map data flow from storage through services to API to UI.
53
+
54
+ ### Pass 3 – tests strategy
55
+
56
+ Explore from test files to understand expected behaviors by reading test names, assertions, and cross-referencing with source code.
57
+
58
+ Each pass outputs domain drafts using this format:
59
+
60
+ ```markdown
61
+ # [Domain Name] - Living Specification
62
+
63
+ ## Overview
64
+ [1-2 paragraph description]
65
+
66
+ ## Key Entities
67
+ [List major classes, services, models]
68
+
69
+ ## Functional Requirements
70
+
71
+ ### FR-001: [Requirement Name]
72
+ - **Description**: [What the system does]
73
+ - **Behavior**: Given [precondition], When [action], Then [result]
74
+ - **Source**: [File path where this is implemented]
75
+
76
+ ### FR-002: ...
77
+ ```
78
+
79
+ Write each functional requirement (FR) per the `writing` skill.
80
+
81
+ ```markdown
82
+ ## Integration Points
83
+ ## Configuration
84
+ ## Constraints & Invariants
85
+ ```
86
+
87
+ ## Phase 2: judge merge (pass 4)
88
+
89
+ Launch a single subagent to merge the 3 independent drafts per domain. It resolves conflicts, deduplicates, and picks the most accurate description for each FR.
90
+
91
+ ## Phase 3: validation (passes 5-7)
92
+
93
+ Launch 3 parallel Explore subagents to validate ALL domains independently – each checks that FRs match actual code behavior.
94
+
95
+ ## Phase 4: correction merge (pass 8)
96
+
97
+ Launch a single subagent to apply validated corrections from Phase 3.
98
+
99
+ ## Phase 5: human review (pass 9)
100
+
101
+ Present a summary to the user. Ask: "Review the specs in `acstack/specs/`. Want to edit any domain before finalizing?"
102
+
103
+ ## Phase 6: finalization (pass 10)
104
+
105
+ Launch a single subagent to finalize all specs. It removes markers, makes sure the formatting is consistent, and re-numbers FR entries.
106
+
107
+ ## Completion
108
+
109
+ Report: the number of domain specs created, the total functional requirements, and the suggested next step.
@@ -0,0 +1,93 @@
1
+ # Cross-device handoff format
2
+
3
+ The bridge spans two devices. When a coding agent on one side needs the
4
+ other side to act, it emits a **handoff block** in this exact shape:
5
+
6
+ ```
7
+ --- BEGIN <WINDOWS|VM>-SIDE HANDOFF ---
8
+ Context: <one line describing what happened and why the other side must act>
9
+ Run on <device>:
10
+ <exact command(s)>
11
+ Expected result:
12
+ <exact success criterion>
13
+ Report back:
14
+ <exact output the requesting side needs>
15
+ --- END <WINDOWS|VM>-SIDE HANDOFF ---
16
+ ```
17
+
18
+ The `WINDOWS-SIDE` or `VM-SIDE` tag names the side that **produced** the
19
+ block, so the other side knows who asked. `Run on <device>` names who
20
+ **executes** it.
21
+
22
+ The other side pastes the block verbatim into another coding agent. If
23
+ the user on that side has no coding agent, they paste it into a plain
24
+ PowerShell or SSH shell instead.
25
+
26
+ ## Worked example 1 – Windows → VM, free a stuck port
27
+
28
+ `doctor.ps1` produces this block on the Windows laptop when
29
+ tunnel-<host>.log shows the stuck-port signature:
30
+
31
+ ```
32
+ --- BEGIN WINDOWS-SIDE HANDOFF (to VM agent on vm1.example.com) ---
33
+ Context: the Playwriter bridge tunnel to vm1.example.com is stuck.
34
+ tunnel-vm1.example.com.log shows "remote port forwarding failed for
35
+ listen port 19988", meaning something on the VM already holds the port.
36
+
37
+ Run on vm1.example.com:
38
+ bash ~/.claude/acstack/bin/vm-free-port.sh
39
+
40
+ Expected result:
41
+ ss -lnt 'sport = :19988' is empty, or vm-free-port.sh refuses and
42
+ tells you the port is held by your own interactive session (in which
43
+ case exit that session and retry).
44
+
45
+ Report back:
46
+ The exit status of vm-free-port.sh and the final ss output.
47
+ --- END WINDOWS-SIDE HANDOFF ---
48
+ ```
49
+
50
+ After the VM side acts, the Windows side confirms recovery with:
51
+
52
+ ```
53
+ Start-ScheduledTask Playwriter-Tunnel-VM1
54
+ doctor.ps1
55
+ ```
56
+
57
+ Success criterion: all rows green.
58
+
59
+ ## Worked example 2 – VM → Windows, restart a tunnel task
60
+
61
+ A VM-side agent produces this block when `agent-browser` cannot reach
62
+ `http://127.0.0.1:19988/json/version`:
63
+
64
+ ```
65
+ --- BEGIN VM-SIDE HANDOFF (to Windows agent) ---
66
+ Context: agent-browser on vm1.example.com cannot reach the CDP endpoint
67
+ at 127.0.0.1:19988 (connection refused). The tunnel appears down.
68
+
69
+ Run on the Windows laptop:
70
+ Stop-ScheduledTask Playwriter-Tunnel-VM1
71
+ Start-ScheduledTask Playwriter-Tunnel-VM1
72
+ .\doctor.ps1
73
+
74
+ Expected result:
75
+ doctor.ps1 exits with all green rows, including "Relay listening on
76
+ 19988" and no tunnel-*.stuck markers.
77
+
78
+ Report back:
79
+ The doctor.ps1 output (paste the table).
80
+ --- END VM-SIDE HANDOFF ---
81
+ ```
82
+
83
+ ## Rules for writing a handoff
84
+
85
+ - **Exact commands.** No "figure out the right flag" – the other side
86
+ may be a human at a shell, not a coding agent.
87
+
88
+ - **One verifiable success criterion.** Not "looks ok".
89
+ - **Small.** One problem per block. If two things break, emit two
90
+ blocks.
91
+
92
+ - **Scrub internal names.** No employer names, people names, internal IP
93
+ ranges, ticket IDs. Use placeholders like `<host>`, `vm1.example.com`.