@laitszkin/apollo-toolkit 3.11.0 → 3.11.2

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 (40) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
  3. package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
  4. package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
  5. package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
  6. package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
  7. package/init-project-html/SKILL.md +56 -20
  8. package/init-project-html/agents/openai.yaml +6 -6
  9. package/init-project-html/lib/atlas/assets/architecture.css +27 -6
  10. package/init-project-html/lib/atlas/assets/viewer.client.js +124 -81
  11. package/init-project-html/lib/atlas/cli.js +48 -15
  12. package/init-project-html/lib/atlas/layout.js +112 -11
  13. package/init-project-html/lib/atlas/render.js +131 -33
  14. package/init-project-html/lib/atlas/schema.js +39 -2
  15. package/init-project-html/references/TEMPLATE_SPEC.md +26 -8
  16. package/init-project-html/sample-demo/resources/project-architecture/assets/architecture.css +27 -6
  17. package/init-project-html/sample-demo/resources/project-architecture/assets/viewer.client.js +124 -81
  18. package/init-project-html/sample-demo/resources/project-architecture/atlas/features/get-invite-codes.yaml +17 -4
  19. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-code-generator.html +23 -7
  20. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-issuance-service.html +45 -13
  21. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/postgresql.html +28 -10
  22. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/public-api.html +33 -13
  23. package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/web-get-invite-ui.html +28 -10
  24. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/postgresql.html +28 -10
  25. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/public-api.html +28 -10
  26. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/registration-service.html +38 -17
  27. package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/web-register-ui.html +29 -11
  28. package/init-project-html/sample-demo/resources/project-architecture/index.html +100 -76
  29. package/init-project-html/scripts/architecture-bootstrap-render.js +16 -0
  30. package/init-project-html/scripts/architecture.js +22 -12
  31. package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
  32. package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
  33. package/package.json +1 -1
  34. package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
  35. package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
  36. package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
  37. package/spec-to-project-html/SKILL.md +25 -16
  38. package/spec-to-project-html/agents/openai.yaml +5 -5
  39. package/spec-to-project-html/references/TEMPLATE_SPEC.md +2 -0
  40. package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
@@ -19,7 +19,7 @@
19
19
  <button type="button" data-pan-zoom="fit" aria-label="Reset view">Fit</button>
20
20
  </div>
21
21
  <div class="atlas-canvas__viewport" data-pan-zoom-viewport>
22
- <svg class="atlas-svg" viewBox="0 0 3989 453" role="img" aria-label="Project architecture atlas" data-atlas-svg="macro">
22
+ <svg class="atlas-svg" viewBox="0 0 3831 521" role="img" aria-label="Project architecture atlas" data-atlas-svg="macro">
23
23
  <defs>
24
24
  <marker id="arrow-call" class="m-arrow m-arrow--call" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker>
25
25
  <marker id="arrow-return" class="m-arrow m-arrow--return" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 Z" /></marker>
@@ -28,118 +28,142 @@
28
28
  </defs>
29
29
  <g transform="translate(24,24)">
30
30
  <g class="m-cluster" data-feature="get-invite-codes">
31
- <rect class="m-cluster__bg" x="40.00" y="40.00" width="1650.00" height="325.00" rx="14" ry="14" />
32
- <text class="m-cluster__title" x="865.00" y="66.00" text-anchor="middle">Get invite codes</text>
31
+ <rect class="m-cluster__bg" x="40.00" y="40.00" width="1576.00" height="393.00" rx="14" ry="14" />
32
+ <text class="m-cluster__title" x="828.00" y="66.00" text-anchor="middle">Get invite codes</text>
33
33
  </g>
34
34
  <g class="m-cluster" data-feature="invite-code-registration">
35
- <rect class="m-cluster__bg" x="2091.00" y="100.00" width="1810.00" height="253.27" rx="14" ry="14" />
36
- <text class="m-cluster__title" x="2996.00" y="126.00" text-anchor="middle">Invite-code registration</text>
35
+ <rect class="m-cluster__bg" x="2017.00" y="133.00" width="1726.00" height="266.40" rx="14" ry="14" />
36
+ <text class="m-cluster__title" x="2880.00" y="159.00" text-anchor="middle">Invite-code registration</text>
37
37
  </g>
38
- <a class="m-node m-node--ui" href="features/get-invite-codes/web-get-invite-ui.html" data-feature="get-invite-codes" data-submodule="web-get-invite-ui">
39
- <rect x="64.00" y="226.80" width="240.00" height="92.00" rx="10" ry="10" />
40
- <text class="m-node__title" x="184.00" y="254.80" text-anchor="middle">web-get-invite-ui</text>
41
- <text class="m-node__kind" x="184.00" y="278.80" text-anchor="middle">UI</text>
42
- <text class="m-node__role" x="184.00" y="304.80" text-anchor="middle">React page that lets a signed-in mem…</text>
38
+ <a class="m-node m-node--ui" href="features/get-invite-codes/web-get-invite-ui.html" data-feature="get-invite-codes" data-submodule="web-get-invite-ui" tabindex="0" aria-label="web-get-invite-ui — React page that lets a signed-in member request a new invite code. — open sub-module page">
39
+ <title>web-get-invite-ui React page that lets a signed-in member request a new invite code.</title>
40
+ <rect x="64.00" y="166.80" width="220.00" height="126.00" rx="10" ry="10" />
41
+ <text class="m-node__title" x="174.00" y="196.80" text-anchor="middle">web-get-invite-ui</text>
42
+ <text class="m-node__kind" x="174.00" y="212.80" text-anchor="middle">UI</text>
43
+ <text class="m-node__role" x="174.00" y="232.80" text-anchor="middle">React page that lets a</text>
44
+ <text class="m-node__role" x="174.00" y="248.80" text-anchor="middle">signed-in member request a new</text>
45
+ <text class="m-node__role" x="174.00" y="264.80" text-anchor="middle">invite code.</text>
43
46
  </a>
44
- <a class="m-node m-node--api" href="features/get-invite-codes/public-api.html" data-feature="get-invite-codes" data-submodule="public-api">
45
- <rect x="511.00" y="226.80" width="240.00" height="92.00" rx="10" ry="10" />
46
- <text class="m-node__title" x="631.00" y="254.80" text-anchor="middle">public-api</text>
47
- <text class="m-node__kind" x="631.00" y="278.80" text-anchor="middle">API</text>
48
- <text class="m-node__role" x="631.00" y="304.80" text-anchor="middle">HTTP boundary for `/api/invites` POS…</text>
47
+ <a class="m-node m-node--api" href="features/get-invite-codes/public-api.html" data-feature="get-invite-codes" data-submodule="public-api" tabindex="0" aria-label="public-api — HTTP boundary for `/api/invites` POST requests. — open sub-module page">
48
+ <title>public-api HTTP boundary for `/api/invites` POST requests.</title>
49
+ <rect x="491.00" y="174.80" width="220.00" height="110.00" rx="10" ry="10" />
50
+ <text class="m-node__title" x="601.00" y="204.80" text-anchor="middle">public-api</text>
51
+ <text class="m-node__kind" x="601.00" y="220.80" text-anchor="middle">API</text>
52
+ <text class="m-node__role" x="601.00" y="240.80" text-anchor="middle">HTTP boundary for</text>
53
+ <text class="m-node__role" x="601.00" y="256.80" text-anchor="middle">`/api/invites` POST requests.</text>
49
54
  </a>
50
- <a class="m-node m-node--service" href="features/get-invite-codes/invite-issuance-service.html" data-feature="get-invite-codes" data-submodule="invite-issuance-service">
51
- <rect x="965.00" y="226.80" width="240.00" height="92.00" rx="10" ry="10" />
52
- <text class="m-node__title" x="1085.00" y="254.80" text-anchor="middle">invite-issuance-service</text>
53
- <text class="m-node__kind" x="1085.00" y="278.80" text-anchor="middle">Service</text>
54
- <text class="m-node__role" x="1085.00" y="304.80" text-anchor="middle">Domain service that mints and persis…</text>
55
+ <a class="m-node m-node--service" href="features/get-invite-codes/invite-issuance-service.html" data-feature="get-invite-codes" data-submodule="invite-issuance-service" tabindex="0" aria-label="invite-issuance-service — Domain service that mints and persists a single invite row per request. — open sub-module page">
56
+ <title>invite-issuance-service Domain service that mints and persists a single invite row per request.</title>
57
+ <rect x="925.00" y="166.80" width="226.00" height="126.00" rx="10" ry="10" />
58
+ <text class="m-node__title" x="1038.00" y="196.80" text-anchor="middle">invite-issuance-service</text>
59
+ <text class="m-node__kind" x="1038.00" y="212.80" text-anchor="middle">service</text>
60
+ <text class="m-node__role" x="1038.00" y="232.80" text-anchor="middle">Domain service that mints and</text>
61
+ <text class="m-node__role" x="1038.00" y="248.80" text-anchor="middle">persists a single invite row per</text>
62
+ <text class="m-node__role" x="1038.00" y="264.80" text-anchor="middle">request.</text>
55
63
  </a>
56
- <a class="m-node m-node--pure-fn" href="features/get-invite-codes/invite-code-generator.html" data-feature="get-invite-codes" data-submodule="invite-code-generator">
57
- <rect x="1426.00" y="129.00" width="240.00" height="92.00" rx="10" ry="10" />
58
- <text class="m-node__title" x="1546.00" y="157.00" text-anchor="middle">invite-code-generator</text>
59
- <text class="m-node__kind" x="1546.00" y="181.00" text-anchor="middle">Pure fn</text>
60
- <text class="m-node__role" x="1546.00" y="207.00" text-anchor="middle">Pure helper that turns random bytes …</text>
64
+ <a class="m-node m-node--pure-fn" href="features/get-invite-codes/invite-code-generator.html" data-feature="get-invite-codes" data-submodule="invite-code-generator" tabindex="0" aria-label="invite-code-generator — Pure helper that turns random bytes into a printable invite code. — open sub-module page">
65
+ <title>invite-code-generator Pure helper that turns random bytes into a printable invite code.</title>
66
+ <rect x="1372.00" y="129.00" width="220.00" height="126.00" rx="10" ry="10" />
67
+ <text class="m-node__title" x="1482.00" y="159.00" text-anchor="middle">invite-code-generator</text>
68
+ <text class="m-node__kind" x="1482.00" y="175.00" text-anchor="middle">pure function</text>
69
+ <text class="m-node__role" x="1482.00" y="195.00" text-anchor="middle">Pure helper that turns random</text>
70
+ <text class="m-node__role" x="1482.00" y="211.00" text-anchor="middle">bytes into a printable invite</text>
71
+ <text class="m-node__role" x="1482.00" y="227.00" text-anchor="middle">code.</text>
61
72
  </a>
62
- <a class="m-node m-node--db" href="features/get-invite-codes/postgresql.html" data-feature="get-invite-codes" data-submodule="postgresql">
63
- <rect x="1426.00" y="245.00" width="240.00" height="92.00" rx="10" ry="10" />
64
- <text class="m-node__title" x="1546.00" y="273.00" text-anchor="middle">postgresql</text>
65
- <text class="m-node__kind" x="1546.00" y="297.00" text-anchor="middle">DB</text>
66
- <text class="m-node__role" x="1546.00" y="323.00" text-anchor="middle">Owns the `invite_codes` table (produ…</text>
73
+ <a class="m-node m-node--db" href="features/get-invite-codes/postgresql.html" data-feature="get-invite-codes" data-submodule="postgresql" tabindex="0" aria-label="postgresql — Owns the `invite_codes` table (producer side of the cross-feature data row). — open sub-module page">
74
+ <title>postgresql Owns the `invite_codes` table (producer side of the cross-feature data row).</title>
75
+ <rect x="1372.00" y="279.00" width="220.00" height="126.00" rx="10" ry="10" />
76
+ <text class="m-node__title" x="1482.00" y="309.00" text-anchor="middle">postgresql</text>
77
+ <text class="m-node__kind" x="1482.00" y="325.00" text-anchor="middle">database</text>
78
+ <text class="m-node__role" x="1482.00" y="345.00" text-anchor="middle">Owns the `invite_codes` table</text>
79
+ <text class="m-node__role" x="1482.00" y="361.00" text-anchor="middle">(producer side of the</text>
80
+ <text class="m-node__role" x="1482.00" y="377.00" text-anchor="middle">cross-feature data row).</text>
67
81
  </a>
68
- <a class="m-node m-node--ui" href="features/invite-code-registration/web-register-ui.html" data-feature="invite-code-registration" data-submodule="web-register-ui">
69
- <rect x="2115.00" y="189.00" width="240.00" height="92.00" rx="10" ry="10" />
70
- <text class="m-node__title" x="2235.00" y="217.00" text-anchor="middle">web-register-ui</text>
71
- <text class="m-node__kind" x="2235.00" y="241.00" text-anchor="middle">UI</text>
72
- <text class="m-node__role" x="2235.00" y="267.00" text-anchor="middle">React page that captures email, pass…</text>
82
+ <a class="m-node m-node--ui" href="features/invite-code-registration/web-register-ui.html" data-feature="invite-code-registration" data-submodule="web-register-ui" tabindex="0" aria-label="web-register-ui — React page that captures email, password, and invite code. — open sub-module page">
83
+ <title>web-register-ui React page that captures email, password, and invite code.</title>
84
+ <rect x="2041.00" y="222.00" width="220.00" height="110.00" rx="10" ry="10" />
85
+ <text class="m-node__title" x="2151.00" y="252.00" text-anchor="middle">web-register-ui</text>
86
+ <text class="m-node__kind" x="2151.00" y="268.00" text-anchor="middle">UI</text>
87
+ <text class="m-node__role" x="2151.00" y="288.00" text-anchor="middle">React page that captures email,</text>
88
+ <text class="m-node__role" x="2151.00" y="304.00" text-anchor="middle">password, and invite code.</text>
73
89
  </a>
74
- <a class="m-node m-node--api" href="features/invite-code-registration/public-api.html" data-feature="invite-code-registration" data-submodule="public-api">
75
- <rect x="2569.00" y="189.00" width="240.00" height="92.00" rx="10" ry="10" />
76
- <text class="m-node__title" x="2689.00" y="217.00" text-anchor="middle">public-api</text>
77
- <text class="m-node__kind" x="2689.00" y="241.00" text-anchor="middle">API</text>
78
- <text class="m-node__role" x="2689.00" y="267.00" text-anchor="middle">HTTP boundary for `/api/register` PO…</text>
90
+ <a class="m-node m-node--api" href="features/invite-code-registration/public-api.html" data-feature="invite-code-registration" data-submodule="public-api" tabindex="0" aria-label="public-api — HTTP boundary for `/api/register` POST requests. — open sub-module page">
91
+ <title>public-api HTTP boundary for `/api/register` POST requests.</title>
92
+ <rect x="2475.00" y="222.00" width="220.00" height="110.00" rx="10" ry="10" />
93
+ <text class="m-node__title" x="2585.00" y="252.00" text-anchor="middle">public-api</text>
94
+ <text class="m-node__kind" x="2585.00" y="268.00" text-anchor="middle">API</text>
95
+ <text class="m-node__role" x="2585.00" y="288.00" text-anchor="middle">HTTP boundary for</text>
96
+ <text class="m-node__role" x="2585.00" y="304.00" text-anchor="middle">`/api/register` POST requests.</text>
79
97
  </a>
80
- <a class="m-node m-node--service" href="features/invite-code-registration/registration-service.html" data-feature="invite-code-registration" data-submodule="registration-service">
81
- <rect x="3101.00" y="229.67" width="240.00" height="92.00" rx="10" ry="10" />
82
- <text class="m-node__title" x="3221.00" y="257.67" text-anchor="middle">registration-service</text>
83
- <text class="m-node__kind" x="3221.00" y="281.67" text-anchor="middle">Service</text>
84
- <text class="m-node__role" x="3221.00" y="307.67" text-anchor="middle">Owns the registration transaction (c…</text>
98
+ <a class="m-node m-node--service" href="features/invite-code-registration/registration-service.html" data-feature="invite-code-registration" data-submodule="registration-service" tabindex="0" aria-label="registration-service — Owns the registration transaction (consumer side of the invite_codes data row). — open sub-module page">
99
+ <title>registration-service Owns the registration transaction (consumer side of the invite_codes data row).</title>
100
+ <rect x="2987.00" y="235.00" width="220.00" height="126.00" rx="10" ry="10" />
101
+ <text class="m-node__title" x="3097.00" y="265.00" text-anchor="middle">registration-service</text>
102
+ <text class="m-node__kind" x="3097.00" y="281.00" text-anchor="middle">service</text>
103
+ <text class="m-node__role" x="3097.00" y="301.00" text-anchor="middle">Owns the registration</text>
104
+ <text class="m-node__role" x="3097.00" y="317.00" text-anchor="middle">transaction (consumer side of</text>
105
+ <text class="m-node__role" x="3097.00" y="333.00" text-anchor="middle">the invite_codes data row).</text>
85
106
  </a>
86
- <a class="m-node m-node--db" href="features/invite-code-registration/postgresql.html" data-feature="invite-code-registration" data-submodule="postgresql">
87
- <rect x="3637.00" y="216.07" width="240.00" height="92.00" rx="10" ry="10" />
88
- <text class="m-node__title" x="3757.00" y="244.07" text-anchor="middle">postgresql</text>
89
- <text class="m-node__kind" x="3757.00" y="268.07" text-anchor="middle">DB</text>
90
- <text class="m-node__role" x="3757.00" y="294.07" text-anchor="middle">Stores `users` rows and applies invi…</text>
107
+ <a class="m-node m-node--db" href="features/invite-code-registration/postgresql.html" data-feature="invite-code-registration" data-submodule="postgresql" tabindex="0" aria-label="postgresql — Stores `users` rows and applies invite-code state transitions inside the registration tx. — open sub-module page">
108
+ <title>postgresql Stores `users` rows and applies invite-code state transitions inside the registration tx.</title>
109
+ <rect x="3499.00" y="228.20" width="220.00" height="126.00" rx="10" ry="10" />
110
+ <text class="m-node__title" x="3609.00" y="258.20" text-anchor="middle">postgresql</text>
111
+ <text class="m-node__kind" x="3609.00" y="274.20" text-anchor="middle">database</text>
112
+ <text class="m-node__role" x="3609.00" y="294.20" text-anchor="middle">Stores `users` rows and applies</text>
113
+ <text class="m-node__role" x="3609.00" y="310.20" text-anchor="middle">invite-code state transitions</text>
114
+ <text class="m-node__role" x="3609.00" y="326.20" text-anchor="middle">inside the registration tx.</text>
91
115
  </a>
92
116
  <g class="m-edge m-edge--data-row" data-edge="cross-issuance-to-postgres-codes">
93
- <path d="M1165.00,260.40 L1175.00,260.40 L1175.00,274.00 L1386.00,274.00" fill="none" marker-end="url(#arrow-data-row)" />
94
- <text class="m-edge__label" x="1275.50" y="290.00" text-anchor="middle">INSERT invite_codes</text>
117
+ <path d="M1111.00,227.60 L1121.00,227.60 L1121.00,334.00 L1306.00,334.00 L1306.00,333.50 L1332.00,333.50" fill="none" marker-end="url(#arrow-data-row)" />
118
+ <text class="m-edge__label" x="1221.50" y="350.00" text-anchor="middle">INSERT invite_codes</text>
95
119
  </g>
96
120
  <g class="m-edge m-edge--data-row" data-edge="cross-postgres-codes-to-registration">
97
- <path d="M1666.00,291.00 L3101.00,291.00" fill="none" marker-end="url(#arrow-data-row)" />
98
- <text class="m-edge__label" x="1890.50" y="307.00" text-anchor="middle">read/consume invite_codes</text>
121
+ <path d="M1592.00,342.00 L2961.00,342.00 L2961.00,319.00 L2987.00,319.00" fill="none" marker-end="url(#arrow-data-row)" />
122
+ <text class="m-edge__label" x="1816.50" y="358.00" text-anchor="middle">read/consume invite_codes</text>
99
123
  </g>
100
124
  <g class="m-edge m-edge--call" data-edge="e-ui-api">
101
- <path d="M304.00,272.80 L511.00,272.80" fill="none" marker-end="url(#arrow-call)" />
102
- <text class="m-edge__label" x="407.50" y="288.80" text-anchor="middle">POST /api/invites</text>
125
+ <path d="M284.00,229.80 L491.00,229.80" fill="none" marker-end="url(#arrow-call)" />
126
+ <text class="m-edge__label" x="387.50" y="245.80" text-anchor="middle">POST /api/invites</text>
103
127
  </g>
104
128
  <g class="m-edge m-edge--call" data-edge="e-api-svc">
105
- <path d="M751.00,272.80 L965.00,272.80" fill="none" marker-end="url(#arrow-call)" />
106
- <text class="m-edge__label" x="858.00" y="288.80" text-anchor="middle">Issue(ctx, userId)</text>
129
+ <path d="M711.00,229.80 L925.00,229.80" fill="none" marker-end="url(#arrow-call)" />
130
+ <text class="m-edge__label" x="818.00" y="245.80" text-anchor="middle">Issue(ctx, userId)</text>
107
131
  </g>
108
132
  <g class="m-edge m-edge--call" data-edge="e-svc-gen">
109
- <path d="M1205.00,245.20 L1215.00,245.20 L1215.00,175.00 L1426.00,175.00" fill="none" marker-end="url(#arrow-call)" />
110
- <text class="m-edge__label" x="1315.50" y="191.00" text-anchor="middle">encode(rand)</text>
133
+ <path d="M1151.00,192.00 L1372.00,192.00" fill="none" marker-end="url(#arrow-call)" />
134
+ <text class="m-edge__label" x="1261.50" y="208.00" text-anchor="middle">encode(rand)</text>
111
135
  </g>
112
136
  <g class="m-edge m-edge--call" data-edge="e-svc-db">
113
- <path d="M1205.00,282.00 L1400.00,282.00 L1400.00,291.00 L1426.00,291.00" fill="none" marker-end="url(#arrow-call)" />
114
- <text class="m-edge__label" x="1315.50" y="298.00" text-anchor="middle">INSERT invite_codes</text>
137
+ <path d="M1151.00,242.40 L1171.00,242.40 L1171.00,342.00 L1372.00,342.00" fill="none" marker-end="url(#arrow-call)" />
138
+ <text class="m-edge__label" x="1261.50" y="358.00" text-anchor="middle">INSERT invite_codes</text>
115
139
  </g>
116
140
  <g class="m-edge m-edge--return" data-edge="e-db-svc-return">
117
- <path d="M1426.00,268.00 L1400.00,268.00 L1400.00,250.00 L1225.00,250.00 L1225.00,263.60 L1205.00,263.60" fill="none" marker-end="url(#arrow-return)" />
118
- <text class="m-edge__label" x="1315.50" y="266.00" text-anchor="middle">rowid</text>
141
+ <path d="M1372.00,310.50 L1346.00,310.50 L1346.00,224.00 L1161.00,224.00 L1161.00,217.20 L1151.00,217.20" fill="none" marker-end="url(#arrow-return)" />
142
+ <text class="m-edge__label" x="1261.50" y="240.00" text-anchor="middle">rowid</text>
119
143
  </g>
120
144
  <g class="m-edge m-edge--call" data-edge="e-ui-api">
121
- <path d="M2355.00,235.00 L2569.00,235.00" fill="none" marker-end="url(#arrow-call)" />
122
- <text class="m-edge__label" x="2462.00" y="227.00" text-anchor="middle">POST /api/register</text>
145
+ <path d="M2261.00,277.00 L2475.00,277.00" fill="none" marker-end="url(#arrow-call)" />
146
+ <text class="m-edge__label" x="2368.00" y="269.00" text-anchor="middle">POST /api/register</text>
123
147
  </g>
124
148
  <g class="m-edge m-edge--call" data-edge="e-api-svc">
125
- <path d="M2809.00,235.00 L3075.00,235.00 L3075.00,260.33 L3101.00,260.33" fill="none" marker-end="url(#arrow-call)" />
126
- <text class="m-edge__label" x="2955.00" y="227.00" text-anchor="middle">RegisterWithInvite(ctx, RegisterInput)</text>
149
+ <path d="M2695.00,277.00 L2987.00,277.00" fill="none" marker-end="url(#arrow-call)" />
150
+ <text class="m-edge__label" x="2841.00" y="269.00" text-anchor="middle">RegisterWithInvite(ctx, RegisterInput)</text>
127
151
  </g>
128
152
  <g class="m-edge m-edge--call" data-edge="e-svc-db-select">
129
- <path d="M3341.00,266.47 L3361.00,266.47 L3361.00,239.27 L3611.00,239.27 L3611.00,252.87 L3637.00,252.87" fill="none" marker-end="url(#arrow-call)" />
130
- <text class="m-edge__label" x="3491.00" y="255.27" text-anchor="middle">SELECT invite_codes FOR UPDATE</text>
153
+ <path d="M3207.00,285.40 L3473.00,285.40 L3473.00,278.60 L3499.00,278.60" fill="none" marker-end="url(#arrow-call)" />
154
+ <text class="m-edge__label" x="3353.00" y="301.40" text-anchor="middle">SELECT invite_codes FOR UPDATE</text>
131
155
  </g>
132
156
  <g class="m-edge m-edge--call" data-edge="e-svc-db-insert">
133
- <path d="M3341.00,284.87 L3371.00,284.87 L3371.00,271.27 L3637.00,271.27" fill="none" marker-end="url(#arrow-call)" />
134
- <text class="m-edge__label" x="3491.00" y="287.27" text-anchor="middle">INSERT users</text>
157
+ <path d="M3207.00,310.60 L3217.00,310.60 L3217.00,317.40 L3473.00,317.40 L3473.00,303.80 L3499.00,303.80" fill="none" marker-end="url(#arrow-call)" />
158
+ <text class="m-edge__label" x="3353.00" y="333.40" text-anchor="middle">INSERT users</text>
135
159
  </g>
136
160
  <g class="m-edge m-edge--call" data-edge="e-svc-db-update">
137
- <path d="M3341.00,303.27 L3611.00,303.27 L3611.00,289.67 L3637.00,289.67" fill="none" marker-end="url(#arrow-call)" />
138
- <text class="m-edge__label" x="3491.00" y="319.27" text-anchor="middle">UPDATE invite_codes.consumed_at</text>
161
+ <path d="M3207.00,335.80 L3217.00,335.80 L3217.00,349.40 L3473.00,349.40 L3473.00,329.00 L3499.00,329.00" fill="none" marker-end="url(#arrow-call)" />
162
+ <text class="m-edge__label" x="3353.00" y="365.40" text-anchor="middle">UPDATE invite_codes.consumed_at</text>
139
163
  </g>
140
164
  <g class="m-edge m-edge--return" data-edge="e-db-svc-return">
141
- <path d="M3637.00,234.47 L3621.00,234.47 L3621.00,207.27 L3351.00,207.27 L3351.00,248.07 L3341.00,248.07" fill="none" marker-end="url(#arrow-return)" />
142
- <text class="m-edge__label" x="3491.00" y="223.27" text-anchor="middle">row | rows_affected</text>
165
+ <path d="M3499.00,253.40 L3217.00,253.40 L3217.00,260.20 L3207.00,260.20" fill="none" marker-end="url(#arrow-return)" />
166
+ <text class="m-edge__label" x="3353.00" y="269.40" text-anchor="middle">row | rows_affected</text>
143
167
  </g>
144
168
  </g>
145
169
  </svg>
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // One-shot helper: `node architecture-bootstrap-render.js render --project <root> ...`
5
+ // Invoked synchronously from architecture.js (legacy open) when index.html is missing
6
+ // so the sync `main()` can still bootstrap an empty tree without duplicating elk layout.
7
+
8
+ const cli = require('../lib/atlas/cli');
9
+
10
+ (async () => {
11
+ const code = await cli.dispatch(process.argv.slice(2));
12
+ process.exit(typeof code === 'number' ? code : 1);
13
+ })().catch((err) => {
14
+ process.stderr.write(`${err && err.stack ? err.stack : err}\n`);
15
+ process.exit(1);
16
+ });
@@ -15,7 +15,7 @@
15
15
 
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
- const { spawn } = require('node:child_process');
18
+ const { spawn, spawnSync } = require('node:child_process');
19
19
 
20
20
  const newCli = require('../lib/atlas/cli');
21
21
 
@@ -47,7 +47,7 @@ Usage:
47
47
  apltk architecture --help Show this help
48
48
 
49
49
  Global flags:
50
- --project <root> Project root (default: nearest ancestor with resources/project-architecture/)
50
+ --project <root> Project root (default: nearest ancestor with resources/project-architecture/, else cwd); missing layout dirs are created when needed
51
51
  --spec <spec_dir> Mutations write to <spec_dir>/architecture_diff/atlas/
52
52
  --no-render Skip auto-render after a mutation
53
53
  --no-open For open/diff: skip launching the browser
@@ -221,14 +221,24 @@ function renderViewer({ changes, projectRoot, outDir }) {
221
221
  }
222
222
 
223
223
  function runOpen(opts, io) {
224
- const projectRoot = opts.projectRoot || findProjectRoot(process.cwd());
224
+ let projectRoot = opts.projectRoot
225
+ ? path.resolve(opts.projectRoot)
226
+ : findProjectRoot(process.cwd());
225
227
  if (!projectRoot) {
226
- io.stderr.write(
227
- `Could not find resources/project-architecture/index.html. Pass --project <root> or generate the atlas via the init-project-html skill.\n`,
228
- );
229
- return 1;
228
+ projectRoot = process.cwd();
230
229
  }
230
+ fs.mkdirSync(path.join(projectRoot, RESOURCES_REL), { recursive: true });
231
231
  const atlas = path.join(projectRoot, ATLAS_REL);
232
+ if (!fs.existsSync(atlas)) {
233
+ const bootstrap = path.join(__dirname, 'architecture-bootstrap-render.js');
234
+ const result = spawnSync(process.execPath, [bootstrap, 'render', '--project', projectRoot, '--no-open'], {
235
+ stdio: 'ignore',
236
+ });
237
+ if (result.status !== 0) {
238
+ io.stderr.write(`Atlas not found and render failed: ${atlas}\n`);
239
+ return 1;
240
+ }
241
+ }
232
242
  if (!fs.existsSync(atlas)) {
233
243
  io.stderr.write(`Atlas not found: ${atlas}\n`);
234
244
  return 1;
@@ -239,13 +249,13 @@ function runOpen(opts, io) {
239
249
  }
240
250
 
241
251
  function runDiff(opts, io) {
242
- const projectRoot = opts.projectRoot || findProjectRoot(process.cwd());
252
+ let projectRoot = opts.projectRoot
253
+ ? path.resolve(opts.projectRoot)
254
+ : findProjectRoot(process.cwd());
243
255
  if (!projectRoot) {
244
- io.stderr.write(
245
- `Could not find resources/project-architecture/index.html. Pass --project <root> or generate the atlas via the init-project-html skill.\n`,
246
- );
247
- return 1;
256
+ projectRoot = process.cwd();
248
257
  }
258
+ fs.mkdirSync(path.join(projectRoot, RESOURCES_REL), { recursive: true });
249
259
  const outDir = opts.out || path.join(projectRoot, DEFAULT_OUT_REL);
250
260
  fs.mkdirSync(outDir, { recursive: true });
251
261
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@laitszkin/apollo-toolkit",
3
- "version": "3.11.0",
3
+ "version": "3.11.2",
4
4
  "description": "Apollo Toolkit npm installer for managed skill copying across Codex, OpenClaw, and Trae.",
5
5
  "license": "MIT",
6
6
  "author": "LaiTszKin",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: spec-to-project-html
3
3
  description: >-
4
- Sync the project HTML architecture atlas to active planning specs by driving `apltk architecture --spec <spec_dir>`. The CLI writes overlay YAML under `<spec_dir>/architecture_diff/atlas/` and re-renders only the affected proposed-after HTML pages, so `apltk architecture diff` can pair before/after by path. Preserve the two-layer rule: macro `index.html` keeps feature clusters with every sub-module visible together; each sub-module page stays self-only (function I/O + variables-with-business-purpose + internal data flow + local errors); feature pages stay lightweight. Read strategy mirrors `init-project-html`: list affected features first, then either dispatch one read-only subagent per affected feature or process them sequentially never load every affected feature's source into the main agent context at once. Ground every declaration in repo evidence; mark `TBD` when code is missing.
4
+ Sync the project HTML architecture atlas to active planning specs by driving `apltk architecture --spec <spec_dir>`. The CLI writes overlay YAML under `<spec_dir>/architecture_diff/atlas/` and re-renders only the affected proposed-after HTML pages — macro SVG and per-sub-module internal-dataflow diagrams stay zoomable just like the base atlas — so `apltk architecture diff` can pair before/after by path. Preserve the two-layer rule and the responsibility split: when subagents are available, each subagent reads ONE affected feature and declares EVERY intra-feature change itself (sub-modules, function / variable / dataflow / error rows, intra-feature edges including error and rollback flows); the main agent only aggregates outbound-boundary summaries and declares cross-feature edges. Without subagents, process features sequentially with the same split. Ground every declaration in repo evidence; mark `TBD` when code is missing.
5
5
  ---
6
6
 
7
7
  # Spec To Project HTML
@@ -27,8 +27,8 @@ description: >-
27
27
  - Function / variable / dataflow / error deltas → corresponding `add` or `remove` verbs scoped to the sub-module.
28
28
  - Edge changes → `edge add` or `edge remove` (use the stable `--id` when available to make the remove unambiguous).
29
29
  - **MUST NOT** drop modules that are still present in code just because the spec omits them — keep them, or rewrite their role/purpose strings to flag "out of spec scope".
30
- - **MUST** scope reads to the **affected feature modules** identified from the spec/design diff (plus any feature owning a cross-feature edge into an affected one). Apply the same context-safe read strategy as `init-project-html` Rule 3:
31
- - **With subagents** — main agent lists affected features first, then dispatches **one read-only subagent per affected feature** to deep-read and return a structured change summary (affected sub-modules, variable / I/O / boundary deltas, edges added/removed). Main agent **only** receives summaries, and only after every subagent reports does it run the CLI verbs in one batched pass (use `--no-render` per verb and a single `apltk architecture render --spec ...` at the end).
30
+ - **MUST** scope reads to the **affected feature modules** identified from the spec/design diff (plus any feature owning a cross-feature edge into an affected one). Apply the same context-safe read strategy as `init-project-html` Rule 3 — **subagents own intra-feature overlay changes; the main agent owns cross-feature seams**:
31
+ - **With subagents (preferred)** — main agent lists affected features first, then dispatches **one write-capable subagent per affected feature**. Each subagent deep-reads its feature and applies every intra-feature overlay mutation itself via `apltk architecture ... --spec <spec_dir> --feature <slug>` (add/remove sub-modules, function / variable / dataflow / error deltas, intra-feature edges — including error / rollback edges and ordered dataflow steps that capture variable state transitions). It returns ONLY a structured change summary of outbound boundaries (cross-feature edges added / changed / removed, with direction and proposed labels) plus its sub-module change list. The main agent never re-reads the feature source; it batches **only cross-feature** `edge add|remove` verbs from the aggregated summaries, then runs `apltk architecture render --spec ...` and `apltk architecture validate --spec ...`.
32
32
  - **Without subagents** — process features one at a time: read one affected feature, **immediately** drive the CLI verbs for that feature (with `--spec ...`). Drop function-level details from memory before reading the next feature.
33
33
  - **Forbidden**: loading every affected feature's source into the main agent's context before declaring — early details get pushed out and overlay declarations contradict each other.
34
34
  - **MUST** run `apltk architecture validate --spec <spec_dir>` after the final mutation. Resolve every reported error before reporting completion.
@@ -40,6 +40,13 @@ description: >-
40
40
  - **Quality**: macro overlay still shows every cross-feature data-row the spec requires; sub-module declarations stay self-only; `apltk architecture diff` opens cleanly with no orphan pages; no dangling edges.
41
41
  - **Output**: touches only `<spec_dir>/architecture_diff/atlas/**` (overlay state) and `<spec_dir>/architecture_diff/**/*.html` (renderer output). Base `resources/project-architecture/` is **NEVER** mutated.
42
42
 
43
+ ## Acceptance criteria (mirrors `init-project-html`)
44
+
45
+ Open the proposed-after viewer (`apltk architecture diff`) and verify both criteria on the overlay pages before reporting completion:
46
+
47
+ 1. **The macro overlay clearly shows the proposed-after feature × sub-module relationships**, including data flow (`--kind data-row`), interaction logic (`--kind call` + `--kind return`), error handling and rollback (`--kind failure`). Any new / changed / removed cross-boundary path the spec implies MUST exist as an edge mutation in the overlay — not as sub-module prose.
48
+ 2. **Each touched sub-module's internal overlay diagram clearly shows the function-level interactions inside it**, including function-to-function flow (`dataflow add --fn <declared-fn>`), variable state transitions (`--reads` / `--writes` referencing declared variables), and the resulting local data flow. If the spec introduces a new function or variable that participates in the flow, declare it via `function add` / `variable add` first, then reference it from the new `dataflow` step so `validate --spec` passes.
49
+
43
50
  ## Workflow
44
51
 
45
52
  ### 1) Resolve spec inputs
@@ -52,29 +59,31 @@ Derive from the spec/design diff which feature modules change: new sub-modules,
52
59
 
53
60
  ### 3) Branch the deep-read + declare by environment (mirrors `init-project-html` Rule 3)
54
61
 
55
- #### 3A) With subagents (preferred)
62
+ #### 3A) With subagents (preferred) — workers patch their feature; main agent patches only cross-feature edges
56
63
 
57
- Dispatch one **read-only subagent per affected feature**, requiring this summary:
64
+ Dispatch one **write-capable subagent per affected feature**, plus the main agent for the macro seams. Each subagent owns every intra-feature overlay write and reports outbound boundaries upward:
58
65
 
59
- > **Feature `<slug>` change summary**
60
- > - Matching spec passages / requirement IDs.
61
- > - Affected sub-modules (added / renamed / retired / I/O changed; new kind/role if changed).
62
- > - Per sub-module: function I/O deltas, variables-with-business-purpose deltas (added/removed/renamed), internal dataflow deltas, errors raised.
63
- > - Boundary changes: new / changed / removed `edge`s (call / return / data-row / failure) with the other-end feature/sub-module slugs.
64
- > - Spec items the code does not yet scaffold: mark as `planned` / `gap` and propose how to surface them (e.g. `--role "planned: ..."`).
66
+ > **Feature `<slug>` subagent contract (overlay)**
67
+ > - Read this feature's affected sub-modules and the cited spec passages / requirement IDs.
68
+ > - Apply every intra-feature overlay mutation via `apltk architecture ... --spec <spec_dir>`:
69
+ > - `submodule add|set|remove` for added / renamed / retired / kind-or-role-changed sub-modules.
70
+ > - `function add|remove`, `variable add|remove`, `dataflow add|remove|reorder`, `error add|remove` for per-sub-module deltas. Order `dataflow` steps so the **variable state transitions** through the new path are visible end-to-end.
71
+ > - Intra-feature `edge add|remove` for every changed function-call / return / data-row / failure / rollback edge between the feature's own sub-modules.
72
+ > - Run `apltk architecture validate --spec <spec_dir>` (scoped check) before returning.
73
+ > - **Return ONLY**: (i) the sub-module change list (slug + change-kind + new kind/role when relevant), (ii) outbound boundary changes (cross-feature edges added / changed / removed, with the other-end `feature/sub` and the suggested `--kind` / `--label`), (iii) any `planned` / `gap` flags so the main agent can mirror them in `meta.summary` if needed.
65
74
 
66
- Main agent collects summaries and runs the CLI in one batched pass:
75
+ Main agent after every subagent returns — declares **only** the cross-feature seams and renders once:
67
76
 
68
77
  ```bash
69
- # add --no-render to every mutation, then render once at the end
70
- apltk architecture submodule add --spec <spec_dir> --feature X --slug Y --kind ... --role "..." --no-render
71
- apltk architecture function add --spec <spec_dir> ... --no-render
72
- apltk architecture edge add --spec <spec_dir> --from X/sub --to Y/sub --kind data-row --label "..." --no-render
78
+ # one verb per cross-feature edge reported by the subagents
79
+ apltk architecture edge add --spec <spec_dir> --from <featA>/<subA> --to <featB>/<subB> --kind call|return|data-row|failure --label "..." --no-render
80
+ apltk architecture edge remove --spec <spec_dir> --id <stable_id> --no-render
73
81
  apltk architecture render --spec <spec_dir>
74
82
  apltk architecture validate --spec <spec_dir>
75
83
  ```
76
84
 
77
85
  - **Pause →** Do every `planned` / `gap` declaration appear consistently across affected sub-modules (e.g. role text + variable purpose strings)? Inconsistency would mislead reviewers.
86
+ - The main agent **MUST NOT** re-declare a subagent's intra-feature components, and **MUST NOT** open source files for any feature it delegated.
78
87
 
79
88
  #### 3B) Without subagents — feature-by-feature read-declare loop
80
89
 
@@ -2,17 +2,17 @@ interface:
2
2
  display_name: "spec-to-project-html"
3
3
  short_description: "Sync the project HTML architecture atlas with active planning specs via `apltk architecture --spec`"
4
4
  default_prompt: >-
5
- Use $spec-to-project-html to read `docs/plans` (spec.md → design.md → contract.md), translate the spec/design delta into `apltk architecture --spec <spec_dir>` CLI verbs, and let the CLI write the overlay YAML under `<spec_dir>/architecture_diff/atlas/` plus the affected proposed-after HTML under `<spec_dir>/architecture_diff/`. NEVER hand-edit files under `architecture_diff/**` — the renderer (owned by $init-project-html) is the only writer. The base atlas under `resources/project-architecture/` is read-only in spec mode.
6
- Read strategy (mirrors $init-project-html Rule 3 to avoid context loss). STEP 1: list AFFECTED feature modules from the spec/design diff (plus any feature owning a cross-feature edge into an affected one) — without diving into function bodies.
5
+ Use $spec-to-project-html to read `docs/plans` (spec.md → design.md → contract.md), translate the spec/design delta into `apltk architecture --spec <spec_dir>` CLI verbs, and let the CLI write the overlay YAML under `<spec_dir>/architecture_diff/atlas/` plus the affected proposed-after HTML under `<spec_dir>/architecture_diff/`. The macro SVG and every sub-module's internal-dataflow diagram in the overlay output stay zoomable, just like the base atlas. NEVER hand-edit files under `architecture_diff/**` — the renderer (owned by $init-project-html) is the only writer. The base atlas under `resources/project-architecture/` is read-only in spec mode.
6
+ Read strategy (mirrors $init-project-html Rule 3 to avoid context loss AND to enforce a hard responsibility split). STEP 1: list AFFECTED feature modules from the spec/design diff (plus any feature owning a cross-feature edge into an affected one) — without diving into function bodies.
7
7
  STEP 2: branch by environment.
8
- (3A, preferred) with subagents, dispatch ONE read-only subagent per affected feature; each returns a structured CHANGE summary (added / renamed / retired sub-modules, function I/O deltas, variables-with-business-purpose deltas, error deltas, outbound edge changes, `planned` / `gap` markers). The main agent collects every summary first, then runs the CLI in one batched pass (`--no-render` per verb plus a single `apltk architecture render --spec <spec_dir>` at the end).
8
+ (3A, preferred) with subagents, dispatch ONE write-capable subagent per affected feature. Each subagent deep-reads its own feature and applies every intra-feature overlay mutation itself via `apltk architecture ... --spec <spec_dir> --feature <slug>`: `submodule add|set|remove`, `function add|remove`, `variable add|remove`, `dataflow add|remove|reorder` (order the steps so the variable state transitions through the new path are visible end-to-end), `error add|remove`, and EVERY intra-feature `edge add|remove` (including failure / rollback edges between the feature's own sub-modules). The subagent returns ONLY a structured CHANGE summary of (i) sub-module changes (slug + change-kind + new kind/role when relevant), (ii) outbound boundary changes (cross-feature edges added / changed / removed with the other-end `feature/sub` and suggested `--kind` / `--label`), and (iii) any `planned` / `gap` flags. The main agent never re-reads source for a delegated feature and never re-declares its intra-feature components — it batches ONLY cross-feature `edge add|remove` from the aggregated summaries, then runs a single `apltk architecture render --spec <spec_dir>` and `apltk architecture validate --spec <spec_dir>`.
9
9
  (3B, no subagents) handle affected features ONE AT A TIME — deep-read feature A, IMMEDIATELY drive the CLI verbs for A with `--spec ...`, drop A's function-level details from memory before moving on.
10
10
  CLI verbs (always pass `--spec <spec_dir>`; kebab-case slugs):
11
11
  `submodule add|set|remove --feature X --slug Y --kind ui|api|service|db|pure-fn|queue|external --role "..."`,
12
12
  `function add|remove --feature X --submodule Y --name fn --in "..." --out "..." --side pure|io|write|tx|lock|network --purpose "..."`,
13
13
  `variable add|remove --feature X --submodule Y --name v --type T --scope call|tx|persist|instance|loop --purpose "..."`,
14
- `dataflow add|remove|reorder --feature X --submodule Y --step "..." [--at N]` (or `--from i --to j` for reorder),
14
+ `dataflow add|remove|reorder --feature X --submodule Y --step "..." [--fn <declared-fn>] [--reads "v1,v2"] [--writes "v3,v4"] [--at N]` (or `--from i --to j` for reorder; every `--fn` / `--reads` / `--writes` value MUST reference a function/variable declared in the merged state for the same sub-module or `validate --spec` fails),
15
15
  `error add|remove --feature X --submodule Y --name ErrCode --when "..." --means "..."`,
16
16
  `edge add|remove --from <feature>[/sub] --to <feature>[/sub] --kind call|return|data-row|failure --label "..." [--id <stable>]`.
17
17
  Removals: `submodule remove` / `feature remove` writes `_removed.yaml`; the renderer emits `_removed.txt` and `apltk architecture diff` classifies the missing pages as `removed`. Rename = remove old slug + add new slug. After the final mutation run `apltk architecture validate --spec <spec_dir>` (must return OK) and `apltk architecture diff` to verify pairing.
18
- Each sub-module page stays self-only — express any cross-boundary interaction as a macro edge via `edge add`, NEVER as sub-module page prose. For `planned` / `gap` items the spec mandates but code does not yet implement, surface the marker in the relevant field (e.g. `--role "planned: TOTP service"`, `--purpose "gap: not wired"`). Anchor every declaration to a concrete spec passage + code path. Report the read strategy used (3A or 3B), the resulting diff counts (`modified` / `added` / `removed`), and any unresolved spec-vs-code gaps.
18
+ Each sub-module page stays self-only — express any cross-boundary interaction as an edge via `edge add`, NEVER as sub-module page prose. For `planned` / `gap` items the spec mandates but code does not yet implement, surface the marker in the relevant field (e.g. `--role "planned: TOTP service"`, `--purpose "gap: not wired"`). Anchor every declaration to a concrete spec passage + code path. Report the read strategy used (3A or 3B), the resulting diff counts (`modified` / `added` / `removed`), and any unresolved spec-vs-code gaps.
@@ -72,6 +72,8 @@ CLI: `apltk architecture submodule add|set|remove --spec <spec_dir> --feature X
72
72
 
73
73
  Each row uses the same fields documented in `init-project-html/references/TEMPLATE_SPEC.md`. Always pass `--spec <spec_dir>` so the write lands in the overlay.
74
74
 
75
+ `dataflow` steps accept the same structured fields in overlay mode — `--fn` must reference a function declared in this overlay (or inherited from base) for the same sub-module; `--reads` / `--writes` must reference variables declared there. `validate --spec <spec_dir>` enforces these references against the **merged** state, so adding a step that names a function/variable the spec also introduces is fine as long as both land in the same overlay.
76
+
75
77
  ### `edge`
76
78
 
77
79
  | Field | Type | Required | Notes |