@seanyao/roll 2026.529.2 → 2026.529.4
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/CHANGELOG.md +18 -0
- package/bin/roll +65 -13
- package/package.json +1 -1
- package/skills/roll-doc/SKILL.md +126 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2026.529.4
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- 非 Claude agent 跑 loop 不再黑屏 `[loop]`
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `roll-doc` 现在追踪数据流和调用链 `[loop]`
|
|
12
|
+
|
|
13
|
+
## v2026.529.3
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- loop 用 kimi 跑不再必崩 `[loop]`
|
|
18
|
+
- 切 agent 后 loop 不再用旧的 `[loop]`
|
|
19
|
+
- loop 不再因提交卡住整轮白做 `[loop]`
|
|
20
|
+
|
|
3
21
|
## v2026.529.2
|
|
4
22
|
|
|
5
23
|
### Added
|
package/bin/roll
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# Roll — AI Agent Convention Manager
|
|
5
5
|
# Single source of truth for how all AI coding agents behave.
|
|
6
6
|
|
|
7
|
-
VERSION="2026.529.
|
|
7
|
+
VERSION="2026.529.4"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -3389,7 +3389,7 @@ _agent_argv() {
|
|
|
3389
3389
|
fi
|
|
3390
3390
|
case "$mode" in
|
|
3391
3391
|
interactive) _AGENT_ARGV=("$_kimi_bin" "$prompt") ;;
|
|
3392
|
-
*) _AGENT_ARGV=("$_kimi_bin"
|
|
3392
|
+
*) _AGENT_ARGV=("$_kimi_bin" -p "$prompt") ;; # FIX-133: kimi-code 无 --quiet,-p 自带 auto 审批
|
|
3393
3393
|
esac ;;
|
|
3394
3394
|
deepseek)
|
|
3395
3395
|
# deepseek has the same argv shape in both modes (positional prompt).
|
|
@@ -5954,6 +5954,10 @@ _write_runner_script() {
|
|
|
5954
5954
|
_write_loop_runner_script() {
|
|
5955
5955
|
local script_path="$1" project_path="$2" cmd="$3" log_path="$4"
|
|
5956
5956
|
local active_start="${5:-10}" active_end="${6:-18}"
|
|
5957
|
+
# FIX-134: skill md path. When set, the inner script rebuilds the agent
|
|
5958
|
+
# command at runtime from the routed cycle agent; when empty it falls back to
|
|
5959
|
+
# the baked command (backwards compatible with callers that omit it).
|
|
5960
|
+
local skill_path="${7:-}"
|
|
5957
5961
|
# FIX-054: terminal preference detection removed. Popup is hard-coded to
|
|
5958
5962
|
# macOS Terminal.app; the 7th positional arg, if any, is ignored for
|
|
5959
5963
|
# backwards compatibility with existing callers.
|
|
@@ -5985,6 +5989,15 @@ _write_loop_runner_script() {
|
|
|
5985
5989
|
agent_cmd="${agent_cmd/--output-format stream-json/--output-format stream-json --add-dir \"\$WT\"}"
|
|
5986
5990
|
fi
|
|
5987
5991
|
local slug; slug=$(_project_slug "$project_path")
|
|
5992
|
+
# FIX-134: emit a runtime command-builder line when skill_path is known, so
|
|
5993
|
+
# the cycle agent is resolved live (routing-aware). Otherwise leave _CYCLE_CMD
|
|
5994
|
+
# empty and the inner script uses the baked fallback command below.
|
|
5995
|
+
local cycle_cmd_line
|
|
5996
|
+
if [[ -n "$skill_path" ]]; then
|
|
5997
|
+
cycle_cmd_line="_CYCLE_CMD=\$(_loop_cycle_agent_cmd \"${skill_path}\" \"\$CYCLE_AGENT\" \"\$WT\" 2>/dev/null || true)"
|
|
5998
|
+
else
|
|
5999
|
+
cycle_cmd_line="_CYCLE_CMD="
|
|
6000
|
+
fi
|
|
5988
6001
|
cat > "$inner_path" << INNER
|
|
5989
6002
|
#!/bin/bash -l
|
|
5990
6003
|
set -o pipefail
|
|
@@ -6018,6 +6031,10 @@ printf '%s:%s\n' "\$\$" "\$(date -u +%s)" > "\$INNER_LOCK"
|
|
|
6018
6031
|
# readers see "still alive in <phase>" during long-running silences (e.g.
|
|
6019
6032
|
# agent_invoke 5-45 min). CURRENT_PHASE is maintained by _phase_begin/_phase_end.
|
|
6020
6033
|
HEARTBEAT_FILE="\${_SHARED_ROOT:-\${HOME}/.shared/roll}/loop/.heartbeat-${slug}"
|
|
6034
|
+
# FIX-136: file-based phase tracking so forked heartbeat child can see phase changes.
|
|
6035
|
+
# CURRENT_PHASE is inherited at fork time — changes after fork are invisible to child.
|
|
6036
|
+
# Writing phase state to a file decouples the heartbeat from fork-time snapshot.
|
|
6037
|
+
HEARTBEAT_PHASE_FILE="\${_SHARED_ROOT:-\${HOME}/.shared/roll}/loop/.phase-${slug}"
|
|
6021
6038
|
CURRENT_PHASE=""
|
|
6022
6039
|
# bash 3.2 (macOS /bin/bash) lacks associative arrays — use namespaced
|
|
6023
6040
|
# variables via 'printf -v' + indirect '\${!VAR}' expansion instead.
|
|
@@ -6029,6 +6046,8 @@ _phase_begin() {
|
|
|
6029
6046
|
local _name="\$1"
|
|
6030
6047
|
printf -v "_PHASE_START_\${_name}" '%s' "\$(date +%s)"
|
|
6031
6048
|
CURRENT_PHASE="\$_name"
|
|
6049
|
+
# FIX-136: write phase+start_ts to file so forked heartbeat child can read it
|
|
6050
|
+
printf '%s %s' "\$_name" "\$(date +%s)" > "\$HEARTBEAT_PHASE_FILE"
|
|
6032
6051
|
_loop_event phase_start "\$_name" "" "" || true
|
|
6033
6052
|
}
|
|
6034
6053
|
_phase_end() {
|
|
@@ -6040,17 +6059,21 @@ _phase_end() {
|
|
|
6040
6059
|
printf -v "_PHASE_DUR_\${_name}" '%s' "\$_dur"
|
|
6041
6060
|
case " \$_PHASE_NAMES_DONE " in *" \$_name "*) ;; *) _PHASE_NAMES_DONE="\${_PHASE_NAMES_DONE} \$_name" ;; esac
|
|
6042
6061
|
CURRENT_PHASE=""
|
|
6062
|
+
# FIX-136: clear phase file so heartbeat knows no active phase
|
|
6063
|
+
echo -n > "\$HEARTBEAT_PHASE_FILE" 2>/dev/null || true
|
|
6043
6064
|
_loop_event phase_end "\$_name" "\${_dur}s" "\$_outcome" || true
|
|
6044
6065
|
}
|
|
6045
6066
|
_heartbeat_writer() {
|
|
6046
6067
|
while true; do
|
|
6047
6068
|
echo "\$(date -u +%s)" > "\$HEARTBEAT_FILE"
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6069
|
+
# FIX-136: read phase from file — CURRENT_PHASE is inherited at fork and
|
|
6070
|
+
# never updated. The parent writes phase+start_ts on _phase_begin, clears
|
|
6071
|
+
# on _phase_end. No phase file = no active phase = skip tick.
|
|
6072
|
+
if [ -f "\$HEARTBEAT_PHASE_FILE" ]; then
|
|
6073
|
+
read -r _hb_phase _hb_start_ts < "\$HEARTBEAT_PHASE_FILE" 2>/dev/null || true
|
|
6074
|
+
if [ -n "\$_hb_phase" ] && [ -n "\$_hb_start_ts" ]; then
|
|
6075
|
+
local _el=\$(( \$(date +%s) - _hb_start_ts ))
|
|
6076
|
+
_loop_event phase_tick "\$_hb_phase" "\${_el}s elapsed" "" 2>/dev/null || true
|
|
6054
6077
|
fi
|
|
6055
6078
|
fi
|
|
6056
6079
|
sleep 60
|
|
@@ -6291,7 +6314,7 @@ _runs_append "failed" 0 "[]" "\$_phases_t" 2>/dev/null || true
|
|
|
6291
6314
|
_keep="\${ROLL_CYCLE_LOG_KEEP:-50}"
|
|
6292
6315
|
( cd "\$_log_dir" && ls -t *.log 2>/dev/null | tail -n +\$((_keep + 1)) | xargs -r rm -f ) 2>/dev/null || true
|
|
6293
6316
|
fi
|
|
6294
|
-
rm -f "\$INNER_LOCK" "\$HEARTBEAT_FILE"
|
|
6317
|
+
rm -f "\$INNER_LOCK" "\$HEARTBEAT_FILE" "\$HEARTBEAT_PHASE_FILE"
|
|
6295
6318
|
exit "\$_rc"
|
|
6296
6319
|
}
|
|
6297
6320
|
trap '_inner_cleanup' EXIT
|
|
@@ -6455,6 +6478,11 @@ export LOOP_SHARED_ROOT="\${_SHARED_ROOT:-\$HOME/.shared/roll}"
|
|
|
6455
6478
|
export ROLL_LOOP_AGENT="\${CYCLE_AGENT:-\$(_project_agent)}"
|
|
6456
6479
|
export ROLL_LOOP_ROUTED_STORY ROLL_LOOP_ROUTED_AGENT ROLL_LOOP_ROUTED_RULE
|
|
6457
6480
|
_phase_begin agent_invoke
|
|
6481
|
+
# FIX-136: non-claude agents (pi/deepseek/kimi) buffer stdout when piped.
|
|
6482
|
+
# Force a pseudo-TTY via script(1) so loop-fmt.py's passthrough receives
|
|
6483
|
+
# output in real time — without this, tmux is black for the entire phase.
|
|
6484
|
+
_AGENT_PTY_PREFIX=""
|
|
6485
|
+
[ "\$ROLL_LOOP_AGENT" != "claude" ] && _AGENT_PTY_PREFIX="script -q /dev/null"
|
|
6458
6486
|
for _attempt in 1 2 3; do
|
|
6459
6487
|
# FIX-068: defensive reset before each attempt — _CYCLE_TIMED_OUT carries
|
|
6460
6488
|
# the SIGTERM result of the previous attempt and would otherwise force an
|
|
@@ -6476,10 +6504,15 @@ for _attempt in 1 2 3; do
|
|
|
6476
6504
|
pkill -KILL -f "\$WT" 2>/dev/null
|
|
6477
6505
|
} ) &
|
|
6478
6506
|
_WATCHDOG_PID=\$!
|
|
6507
|
+
${cycle_cmd_line}
|
|
6508
|
+
# FIX-134: prefer the runtime-rebuilt command (routing-aware); fall back to
|
|
6509
|
+
# the baked command (project agent at \`roll loop on\` time) when empty.
|
|
6479
6510
|
if [ -f "\$FMT" ]; then
|
|
6480
|
-
( cd "\$WT" &&
|
|
6511
|
+
if [ -n "\$_CYCLE_CMD" ]; then ( cd "\$WT" && eval \$_AGENT_PTY_PREFIX "\$_CYCLE_CMD" ) | python3 "\$FMT"
|
|
6512
|
+
else ( cd "\$WT" && \$_AGENT_PTY_PREFIX ${agent_cmd} ) | python3 "\$FMT"; fi
|
|
6481
6513
|
else
|
|
6482
|
-
( cd "\$WT" &&
|
|
6514
|
+
if [ -n "\$_CYCLE_CMD" ]; then ( cd "\$WT" && eval \$_AGENT_PTY_PREFIX "\$_CYCLE_CMD" )
|
|
6515
|
+
else ( cd "\$WT" && \$_AGENT_PTY_PREFIX ${agent_cmd} ); fi
|
|
6483
6516
|
fi
|
|
6484
6517
|
_exit=\$?
|
|
6485
6518
|
kill "\$_WATCHDOG_PID" 2>/dev/null
|
|
@@ -6939,7 +6972,7 @@ _install_launchd_plists() {
|
|
|
6939
6972
|
local cmd; cmd=$(_agent_skill_cmd "${sd}/${skill}/SKILL.md" 2>/dev/null || echo "roll loop now")
|
|
6940
6973
|
|
|
6941
6974
|
if [[ "$svc" == "loop" ]]; then
|
|
6942
|
-
_write_loop_runner_script "$runner" "$project_path" "cd \"${project_path}\" && ${cmd}" "$log" "$active_start" "$active_end"
|
|
6975
|
+
_write_loop_runner_script "$runner" "$project_path" "cd \"${project_path}\" && ${cmd}" "$log" "$active_start" "$active_end" "${sd}/${skill}/SKILL.md"
|
|
6943
6976
|
else
|
|
6944
6977
|
_write_runner_script "$runner" "$project_path" "cd \"${project_path}\" && ${cmd}" "$log"
|
|
6945
6978
|
fi
|
|
@@ -6984,7 +7017,9 @@ _install_launchd_plists() {
|
|
|
6984
7017
|
|
|
6985
7018
|
_agent_skill_cmd() {
|
|
6986
7019
|
local skill_path="$1"
|
|
6987
|
-
|
|
7020
|
+
# FIX-134: accept an explicit agent (loop routing passes the resolved cycle
|
|
7021
|
+
# agent); default to the project agent for non-routed callers.
|
|
7022
|
+
local agent="${2:-$(_project_agent)}"
|
|
6988
7023
|
local strip="awk 'NR==1 && /^---$/{skip=1;next} skip && /^---$/{skip=0;next} !skip{print}' '${skill_path}'"
|
|
6989
7024
|
_agent_argv "$agent" plain "__PROMPT__" || {
|
|
6990
7025
|
err "Unknown agent '${agent}'. Run: roll agent use <claude|kimi|deepseek|pi|openai|codex|opencode|qwen|gemini>"
|
|
@@ -7005,6 +7040,23 @@ _agent_skill_cmd() {
|
|
|
7005
7040
|
echo "${out} \"\$(${strip})\""
|
|
7006
7041
|
}
|
|
7007
7042
|
|
|
7043
|
+
# FIX-134: build the full per-cycle agent command at RUNTIME, routing-aware.
|
|
7044
|
+
# The loop inner script calls this with the resolved cycle agent (CYCLE_AGENT =
|
|
7045
|
+
# ROLL_LOOP_ROUTED_AGENT or project agent) so routing actually switches the
|
|
7046
|
+
# executed binary — instead of running a constant baked at `roll loop on` time.
|
|
7047
|
+
# Reproduces the claude-only verbose / stream-json / add-dir enhancements that
|
|
7048
|
+
# _write_loop_runner_script previously baked into the runner.
|
|
7049
|
+
_loop_cycle_agent_cmd() {
|
|
7050
|
+
local skill_path="$1" agent="${2:-$(_project_agent)}" wt="${3:-$WT}"
|
|
7051
|
+
[ -n "$skill_path" ] || return 1
|
|
7052
|
+
local cmd; cmd=$(_agent_skill_cmd "$skill_path" "$agent") || return 1
|
|
7053
|
+
cmd="${cmd/claude -p/claude -p --verbose --dangerously-skip-permissions --output-format stream-json}"
|
|
7054
|
+
if [[ "$cmd" == *"--output-format stream-json"* ]]; then
|
|
7055
|
+
cmd="${cmd/--output-format stream-json/--output-format stream-json --add-dir \"$wt\"}"
|
|
7056
|
+
fi
|
|
7057
|
+
printf '%s' "$cmd"
|
|
7058
|
+
}
|
|
7059
|
+
|
|
7008
7060
|
cmd_loop() {
|
|
7009
7061
|
local subcmd="${1:-status}"; shift || true
|
|
7010
7062
|
case "$subcmd" in
|
package/package.json
CHANGED
package/skills/roll-doc/SKILL.md
CHANGED
|
@@ -7,7 +7,7 @@ description: "Legacy project documentation automation. Scans all docs, builds/up
|
|
|
7
7
|
|
|
8
8
|
# roll-doc
|
|
9
9
|
|
|
10
|
-
Four-phase legacy documentation automation: scan → index → gap analysis → fill.
|
|
10
|
+
Four-phase legacy documentation automation (plus deep-read Phase 3b): scan → index → gap analysis → fill (directory-level) → deep read (cross-directory topics).
|
|
11
11
|
|
|
12
12
|
Works on any project root. No manual mode switching — reads the project state and decides what to do.
|
|
13
13
|
|
|
@@ -162,6 +162,125 @@ Only include lines for directories that already exist in the project.
|
|
|
162
162
|
|
|
163
163
|
Do not fabricate details — infer only from source files actually read.
|
|
164
164
|
|
|
165
|
+
## Phase 3b — Deep Read
|
|
166
|
+
|
|
167
|
+
Deep-read phase that builds a full project symbol table (without truncation) and auto-detects
|
|
168
|
+
cross-directory topics that directory-level Phase 3 alone cannot discover.
|
|
169
|
+
|
|
170
|
+
**Trigger conditions** — Phase 3b runs when either is true:
|
|
171
|
+
- Phase 2 found any gap (module or special gap)
|
|
172
|
+
- The project exhibits code characteristics that Phase 3a cannot capture:
|
|
173
|
+
cross-directory import chains spanning ≥ 3 directories, state enums referenced by
|
|
174
|
+
multiple files, external URL/endpoint calls, or CI pipeline configuration files
|
|
175
|
+
|
|
176
|
+
Pure documentation-only projects (no source code gaps, no code characteristics) skip Phase 3b.
|
|
177
|
+
|
|
178
|
+
### Step 1 — Build Symbol Table
|
|
179
|
+
|
|
180
|
+
Read every source file **in full** (no truncation). The existing Phase 3 "up to 20 source files"
|
|
181
|
+
limit does not apply — Phase 3b builds a complete project symbol table for downstream topic
|
|
182
|
+
detection to reason across files without missing logic.
|
|
183
|
+
|
|
184
|
+
**Exclusion directories** (same as Phase 1):
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
node_modules/ .git/ dist/ build/ .shared/ .roll/dream/ .roll/briefs/
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Symbol table fields:**
|
|
191
|
+
|
|
192
|
+
| Field | Content |
|
|
193
|
+
|-------|---------|
|
|
194
|
+
| `exports` | class / interface / type / function / const declarations, per file |
|
|
195
|
+
| `imports` | source file → target file mapping (dependency graph edges) |
|
|
196
|
+
| `enums` | enum declarations with enumerated values, per file |
|
|
197
|
+
| `external_urls` | `fetch(...)` / `axios` / `http.*` calls, `API_ENDPOINT` / `*_URL` / `*_HOST` constants, hardcoded `https?://` strings (exclude comments and test fixtures) |
|
|
198
|
+
| `configs` | CI workflow YAML files, build config paths, test framework config file paths |
|
|
199
|
+
|
|
200
|
+
**`--dry-run` behavior:** print symbol table summary counts per category (e.g. "exports: 42, imports: 156, enums: 7, external_urls: 4, configs: 3") plus top-N examples per category. Write nothing to disk.
|
|
201
|
+
|
|
202
|
+
**`--force` behavior:** unchanged — `--force` only affects draft generation (Phase 3/3b output files).
|
|
203
|
+
The symbol table itself is rebuilt from scratch on every run regardless of flags.
|
|
204
|
+
|
|
205
|
+
### Step 2 — Topic Detection
|
|
206
|
+
|
|
207
|
+
Using the symbol table from Step 1, detect cross-directory topics. Each topic type has a
|
|
208
|
+
detection rule and a target output file. Skip any topic whose target file already exists
|
|
209
|
+
(unless `--force`). Skip any topic whose detection rule finds no matches.
|
|
210
|
+
|
|
211
|
+
| Topic | Detection Rule | Output |
|
|
212
|
+
|-------|---------------|--------|
|
|
213
|
+
| 数据流 / 调用链 | Entry files (`bin/`, `cmd/`, `main.*`, `index.*`) → trace import chain to leaf nodes; require ≥ 1 chain spanning ≥ 3 directories | `docs/data-flows.md` |
|
|
214
|
+
| 状态机 | Enums matching `*State` / `*Status` referenced by ≥ 2 source files | `docs/state-machines.md` |
|
|
215
|
+
| 外部集成 | `external_urls` entries from symbol table (exclude comment/test-fixture matches) | `docs/integrations.md` |
|
|
216
|
+
| 部署管线 | `.github/workflows/*.yml` / `.gitlab-ci.yml` / `circle.yml` / `Jenkinsfile` present, with deploy URL patterns detected | `docs/deployment.md` |
|
|
217
|
+
| Agent 入口 (AGENTS.md) | Project root has no `AGENTS.md` AND `src/` (or equivalent source root) has ≥ 3 subdirectories | `AGENTS.md` |
|
|
218
|
+
| 高引用目录 | Directory imported by ≥ 5 other source files, even if directory itself has < 3 source files | `<dir>/README.md` |
|
|
219
|
+
|
|
220
|
+
#### Data Flow / Import Chain Tracing
|
|
221
|
+
|
|
222
|
+
**Entry point selection:** start from entry files — any file matching patterns:
|
|
223
|
+
`bin/*`, `cmd/**/*`, `main.*` (e.g. `main.ts`, `main.py`), `index.*` (e.g. `index.ts`, `index.jsx`),
|
|
224
|
+
`App.*`, `server.*`. Exclude `node_modules/`, `dist/`, `build/`, test files (`*.test.*`, `*.spec.*`, `tests/`).
|
|
225
|
+
|
|
226
|
+
**Chain construction:**
|
|
227
|
+
1. For each entry file, read its imports from the symbol table's `imports` field.
|
|
228
|
+
2. Recursively follow each imported file to its own imports, building a directed call graph.
|
|
229
|
+
3. Stop at leaf nodes — files that import nothing or whose imports all point to:
|
|
230
|
+
- External packages (node_modules / stdlib / third-party)
|
|
231
|
+
- Already-visited nodes (cycle termination)
|
|
232
|
+
4. Each distinct path from an entry file to a leaf is one call chain.
|
|
233
|
+
|
|
234
|
+
**Threshold (cross-directory filter):**
|
|
235
|
+
A call chain is valid for inclusion only if it spans **≥ 3 distinct source directories**.
|
|
236
|
+
Count based on the unique parent directories of files in the chain:
|
|
237
|
+
`src/cli/main.ts → src/commands/build.ts → lib/utils/fs.ts` = 3 directories ✅
|
|
238
|
+
`src/cli/main.ts → src/cli/config.ts → lib/utils/fs.ts` = 2 directories ❌
|
|
239
|
+
If no chain meets the ≥ 3 directory threshold, skip generation entirely (no empty `docs/data-flows.md`).
|
|
240
|
+
|
|
241
|
+
**Output document structure** (`docs/data-flows.md`):
|
|
242
|
+
|
|
243
|
+
```markdown
|
|
244
|
+
> **Draft** — auto-generated by roll-doc on YYYY-MM-DD. Review before treating as authoritative.
|
|
245
|
+
|
|
246
|
+
# Data Flows
|
|
247
|
+
|
|
248
|
+
## Flow: {short descriptive name from entry file purpose}
|
|
249
|
+
|
|
250
|
+
**Entry point:** `{entry_file}:{line}`
|
|
251
|
+
**Directories spanned:** N ({comma-separated list})
|
|
252
|
+
|
|
253
|
+
### Complete Call Chain
|
|
254
|
+
|
|
255
|
+
{entry_file}
|
|
256
|
+
→ import {symbol} from "{file}" ({line})
|
|
257
|
+
→ import {symbol} from "{file}" ({line})
|
|
258
|
+
→ ... (leaf node)
|
|
259
|
+
|
|
260
|
+
### Files Involved
|
|
261
|
+
|
|
262
|
+
| Step | File:Line | Function / Method |
|
|
263
|
+
|------|-----------|-------------------|
|
|
264
|
+
| 1 | `path/to/file:12` | `functionName` |
|
|
265
|
+
| 2 | `path/to/file:34` | `otherFunction` |
|
|
266
|
+
| ... | ... | ... |
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- Sort flows by number of directories spanned, descending (widest cross-cutting flow first).
|
|
270
|
+
- If an entry file produces multiple distinct call chains, list each one as a separate flow entry.
|
|
271
|
+
- `file:line` annotations must come from actual symbol table records — do not fabricate.
|
|
272
|
+
|
|
273
|
+
**Idempotency:** skip (do not overwrite) if `docs/data-flows.md` already exists, unless `--force`.
|
|
274
|
+
|
|
275
|
+
### Step 3 — Source Annotations
|
|
276
|
+
|
|
277
|
+
Every topic document generated in Step 2 must cite `file:line` for each claim (function call,
|
|
278
|
+
endpoint URL, state transition, CI job, import path). Annotations must come from actual
|
|
279
|
+
symbol table records — do not fabricate line numbers. Follows the same "Do not fabricate"
|
|
280
|
+
rule as Phase 3.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
165
284
|
## Phase 4 — Report
|
|
166
285
|
|
|
167
286
|
After all phases complete, output a summary:
|
|
@@ -181,6 +300,12 @@ Phase 3 — Fill
|
|
|
181
300
|
N drafts generated: [list of paths]
|
|
182
301
|
N skipped (already exist; use --force to regenerate)
|
|
183
302
|
|
|
303
|
+
Phase 3b — Deep Read
|
|
304
|
+
Symbol table: exports(N) imports(N) enums(N) external_urls(N) configs(N)
|
|
305
|
+
N topic documents generated: [list of paths and types]
|
|
306
|
+
N topics skipped (no matches or already exist; use --force to regenerate)
|
|
307
|
+
(if no topics generated: "no subject-level drafts generated")
|
|
308
|
+
|
|
184
309
|
📋 Review priority (largest / most active modules first):
|
|
185
310
|
1. src/commands/README.md — 8 source files
|
|
186
311
|
2. docs/CONVENTIONS.md — 6 patterns detected
|