@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.
- package/CHANGELOG.md +29 -0
- package/analyse-app-logs/scripts/__pycache__/filter_logs_by_time.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/log_cli_utils.cpython-312.pyc +0 -0
- package/analyse-app-logs/scripts/__pycache__/search_logs.cpython-312.pyc +0 -0
- package/docs-to-voice/scripts/__pycache__/docs_to_voice.cpython-312.pyc +0 -0
- package/generate-spec/scripts/__pycache__/create-specscpython-312.pyc +0 -0
- package/init-project-html/SKILL.md +56 -20
- package/init-project-html/agents/openai.yaml +6 -6
- package/init-project-html/lib/atlas/assets/architecture.css +27 -6
- package/init-project-html/lib/atlas/assets/viewer.client.js +124 -81
- package/init-project-html/lib/atlas/cli.js +48 -15
- package/init-project-html/lib/atlas/layout.js +112 -11
- package/init-project-html/lib/atlas/render.js +131 -33
- package/init-project-html/lib/atlas/schema.js +39 -2
- package/init-project-html/references/TEMPLATE_SPEC.md +26 -8
- package/init-project-html/sample-demo/resources/project-architecture/assets/architecture.css +27 -6
- package/init-project-html/sample-demo/resources/project-architecture/assets/viewer.client.js +124 -81
- package/init-project-html/sample-demo/resources/project-architecture/atlas/features/get-invite-codes.yaml +17 -4
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-code-generator.html +23 -7
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/invite-issuance-service.html +45 -13
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/postgresql.html +28 -10
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/public-api.html +33 -13
- package/init-project-html/sample-demo/resources/project-architecture/features/get-invite-codes/web-get-invite-ui.html +28 -10
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/postgresql.html +28 -10
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/public-api.html +28 -10
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/registration-service.html +38 -17
- package/init-project-html/sample-demo/resources/project-architecture/features/invite-code-registration/web-register-ui.html +29 -11
- package/init-project-html/sample-demo/resources/project-architecture/index.html +100 -76
- package/init-project-html/scripts/architecture-bootstrap-render.js +16 -0
- package/init-project-html/scripts/architecture.js +22 -12
- package/katex/scripts/__pycache__/render_katex.cpython-312.pyc +0 -0
- package/open-github-issue/scripts/__pycache__/open_github_issue.cpython-312.pyc +0 -0
- package/package.json +1 -1
- package/read-github-issue/scripts/__pycache__/find_issues.cpython-312.pyc +0 -0
- package/read-github-issue/scripts/__pycache__/read_issue.cpython-312.pyc +0 -0
- package/resolve-review-comments/scripts/__pycache__/review_threads.cpython-312.pyc +0 -0
- package/spec-to-project-html/SKILL.md +25 -16
- package/spec-to-project-html/agents/openai.yaml +5 -5
- package/spec-to-project-html/references/TEMPLATE_SPEC.md +2 -0
- package/text-to-short-video/scripts/__pycache__/enforce_video_aspect_ratio.cpython-312.pyc +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,35 @@ All notable changes to this repository are documented in this file.
|
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
+
## [v3.11.2] - 2026-05-11
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `apltk architecture` / `resolveProjectRoot`: auto-create `resources/project-architecture/` on every command; when no atlas marker is found walking parent directories, use the current working directory as the project root (explicit `--project` still wins). `open` renders a fresh `index.html` when the file is missing so an empty tree bootstraps in one step.
|
|
18
|
+
- Legacy `init-project-html/scripts/architecture.js` `open` / `diff`: same directory creation and one-shot `render` bootstrap via `architecture-bootstrap-render.js` when `index.html` is absent.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- `init-project-html/scripts/architecture-bootstrap-render.js` helper invoked by the legacy sync `open` path.
|
|
23
|
+
- Tests: `test/atlas-cli.test.js` covers `--project` on a bare directory (layout + `index.html`); `test/architecture-script.test.js` expects legacy `open` to exit 0 when `index.html` is missing after bootstrap.
|
|
24
|
+
|
|
25
|
+
## [v3.11.1] - 2026-05-11
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `dataflow add` optional `--fn`, `--reads`, and `--writes` (comma-separated variable names): each internal step can reference a declared function and read/write declared variables in the same sub-module; `validate` rejects unknown names. The renderer draws a function pill plus reads/writes chips on sub-module internal flow diagrams.
|
|
30
|
+
- `init-project-html` / `spec-to-project-html`: **Acceptance criteria** for macro edges (call/return/data-row/failure) and for sub-module diagrams (function flow + variable state); Rule 2 and CLI verb table updated; `TEMPLATE_SPEC.md` documents object-shaped `dataflow` steps and new CSS hooks.
|
|
31
|
+
- Macro atlas UX: each sub-module rectangle is an `<a href="features/<feature>/<sub>.html">` with `aria-label`, nested SVG `<title>` tooltip, `cursor: pointer`, and clearer hover/focus styles.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- `layout.js`: `measureSubmodule()` computes per-node width/height from slug, kind label, and `role` so elkjs lays out boxes that fit wrapped role text (replaces fixed 240×92 and the old single-line truncation).
|
|
36
|
+
- `render.js`: macro SVG draws slug, kind, and multi-line role using the same `measureSubmodule()` output as layout; sub-dataflow SVG renders enriched steps with fn pill and reads/writes chips (from prior work in this release train, now validated and documented).
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- `viewer.client.js`: defer pointer capture until the pointer moves past a small drag threshold so clicks on macro sub-module links navigate; swallow the synthetic click after an actual drag so pan does not accidentally open a page.
|
|
41
|
+
|
|
13
42
|
## [v3.11.0] - 2026-05-11
|
|
14
43
|
|
|
15
44
|
### Added
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: init-project-html
|
|
3
3
|
description: >-
|
|
4
|
-
Declare the project HTML architecture atlas through `apltk architecture` CLI verbs instead of hand-writing SVG/HTML.
|
|
4
|
+
Declare the project HTML architecture atlas through `apltk architecture` CLI verbs instead of hand-writing SVG/HTML. Two acceptance criteria gate completion: (1) the macro diagram clearly shows feature × sub-module relationships — data flow, call/return interaction logic, error handling, rollback — all expressed as `--kind call|return|data-row|failure` edges; (2) each sub-module's internal diagram clearly shows function-level interactions inside it — function-to-function flow via `dataflow add --fn <declared-fn>`, variable state transitions via `--reads`/`--writes` referencing declared variables, and the resulting local data flow. The tool owns layout, CSS, and pan/zoom for both diagrams; `validate` rejects any step that references an undeclared function or variable. With subagents, each owns one feature and declares every intra-feature interaction itself; the main agent only declares cross-feature edges. Anchor every declaration to repo evidence.
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Init Project HTML
|
|
@@ -19,25 +19,36 @@ description: >-
|
|
|
19
19
|
|
|
20
20
|
The atlas state lives in `resources/project-architecture/atlas/` (`atlas.index.yaml` + per-feature YAMLs). Every change goes through `apltk architecture <verb> ...`. After any mutation, the CLI re-renders `resources/project-architecture/**/*.html` with the correct layout, CSS, pan/zoom, and ARIA — agents **MUST NOT** edit those HTML files by hand or invent additional ones.
|
|
21
21
|
|
|
22
|
-
### Rule 2 — Sub-module pages describe only themselves
|
|
22
|
+
### Rule 2 — Sub-module pages describe only themselves; the internal-dataflow diagram is zoomable and structurally typed
|
|
23
23
|
|
|
24
24
|
What the CLI emits on each sub-module page is fixed:
|
|
25
25
|
|
|
26
26
|
- function I/O table (`function add --feature X --submodule Y --name fn --in ... --out ... --side ... --purpose ...`),
|
|
27
27
|
- variables-with-business-purpose table (`variable add --name v --type T --scope call|tx|persist|instance|loop --purpose ...`),
|
|
28
|
-
- internal dataflow steps
|
|
28
|
+
- internal dataflow diagram — ordered steps the renderer lays out as a numbered top-to-bottom flow inside a pan/zoom viewport with +/−/Fit controls, exactly like the macro SVG. Each step is `dataflow add --step "..." [--fn <declared-fn>] [--reads "v1,v2"] [--writes "v3,v4"]`. The renderer surfaces `--fn` as a pill at the top of the step box, `--reads` as a green chip on the bottom-left, and `--writes` as an orange chip on the bottom-right — so a single SVG simultaneously shows the function-to-function flow inside the sub-module AND the variable state transitions across the run,
|
|
29
29
|
- local errors (`error add --name ErrX --when ... --means ...`).
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Schema rules the CLI enforces on every `dataflow add` (so the diagram never lies):
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
- `--fn <name>` must match a function declared **in the same sub-module** via `function add`. If it does not, `validate` fails — declare the function first.
|
|
34
|
+
- Every name in `--reads` / `--writes` must match a variable declared **in the same sub-module** via `variable add`. If it does not, `validate` fails — declare the variable first.
|
|
35
|
+
- `--fn` / `--reads` / `--writes` are optional but additive: omitting them is allowed for purely descriptive steps, but for any non-trivial sub-module the function-and-variable structure **MUST** be filled in so the diagram conveys the function-to-function flow and the variable state transitions.
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
Cross-boundary narratives ("I call X", "Y calls me") **MUST** be expressed as edges (`edge add --from X/sub --to Y/sub --kind call|return|data-row|failure --label ...`), never as sub-module page prose. The CLI enforces this by design — there is no verb to add cross-boundary text to a sub-module page.
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
- **Without subagents:** process features **one at a time** — read feature A end-to-end, then declare it via the CLI (`feature add`, `submodule add`, `function add`, `variable add`, `dataflow add`, `error add`, intra-feature `edge add`). For cross-feature edges that point at not-yet-declared features, declare the placeholder feature with `feature add --slug <future> --title <future>` first, then add the edge; subsequent passes set the real title/story. Drop A's function-level details from working memory before reading feature B.
|
|
39
|
+
### Rule 3 — Read order: never let the codebase wipe the context window; subagents own their own feature
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
Real production codebases dwarf the main agent's context. Reading the whole repo before declaring pushes early details out by the end, making macro edges and sub-module declarations contradict each other. The split below also draws a hard responsibility line between the worker and the orchestrator. **MUST** follow one of:
|
|
42
|
+
|
|
43
|
+
- **With subagents (preferred):** first enumerate the **feature-module list** (slug + entry + boundary resources only). Then dispatch **one (write-capable) subagent per feature**. Each subagent:
|
|
44
|
+
- deep-reads its own feature,
|
|
45
|
+
- declares **everything intra-feature** itself via `apltk architecture ... --feature <slug>` (sub-modules; per sub-module functions / variables / internal dataflow / errors; **every intra-feature edge** — function calls between sub-modules, error edges, rollback / compensation edges; variable state transitions are captured as ordered `dataflow add` steps on each sub-module),
|
|
46
|
+
- returns ONLY a structured summary of (i) the sub-module list and (ii) the **outbound boundaries** the main agent needs to wire (calls / data-rows / failure edges to other features' sub-modules, including direction and the consuming/producing endpoints).
|
|
47
|
+
|
|
48
|
+
The main agent never re-reads source for that feature. It collects every subagent's outbound-boundary summary and declares **only the cross-feature edges** (and any cross-feature actors / meta) itself, then runs `apltk architecture validate`.
|
|
49
|
+
- **Without subagents:** process features **one at a time** — read feature A end-to-end, declare its sub-modules, functions, variables, internal dataflow, errors, and intra-feature edges via the CLI, then move on. For cross-feature edges that point at not-yet-declared features, declare the placeholder feature with `feature add --slug <future> --title <future>` first, then add the edge; subsequent passes set the real title/story. Drop A's function-level details from working memory before reading feature B.
|
|
50
|
+
|
|
51
|
+
Both paths share one invariant: at any moment the agent doing the deep read only holds *that feature's details + cross-feature boundary notes*. The main agent's working set, in subagent mode, is **boundaries only**. Run `apltk architecture validate` after the last feature to catch dangling edges, unknown endpoints, or missing required fields.
|
|
41
52
|
|
|
42
53
|
### Rule 4 — Evidence over invention
|
|
43
54
|
|
|
@@ -51,6 +62,22 @@ Both paths share one invariant: at any moment the main agent only holds *current
|
|
|
51
62
|
- **Quality**: macro SVG carries every cross-feature data-row edge that exists in the system; sub-module declarations are self-only; pan/zoom + Fit work in the rendered HTML; `apltk architecture validate` returns OK.
|
|
52
63
|
- **Output**: `resources/project-architecture/atlas/` (YAML state) + `resources/project-architecture/**/*.html` (renderer output) — both managed by the CLI.
|
|
53
64
|
|
|
65
|
+
## Acceptance criteria
|
|
66
|
+
|
|
67
|
+
The atlas is only complete when both of the following are demonstrably true on the rendered HTML (open `resources/project-architecture/index.html` and one representative sub-module page to verify):
|
|
68
|
+
|
|
69
|
+
1. **Macro diagram clearly shows the relationships between features and sub-modules**, including:
|
|
70
|
+
- **Data flow** — every cross-feature DB row, queue topic, or cache key hand-off is a `--kind data-row` edge between the producing and consuming sub-modules (not free-form prose).
|
|
71
|
+
- **Interaction logic** — synchronous request/response paths are paired `--kind call` (outbound) and `--kind return` (response) edges with a label that names the call site or response shape.
|
|
72
|
+
- **Error handling and rollback** — every failure / compensation / retry path that crosses a sub-module boundary is a `--kind failure` edge with a label that names what is being rolled back or compensated.
|
|
73
|
+
- No interaction is captured *only* in sub-module page prose — if it crosses a sub-module boundary it MUST appear as an edge so the macro SVG carries it.
|
|
74
|
+
2. **Each sub-module's internal diagram clearly shows the function-level interactions inside the sub-module**, including:
|
|
75
|
+
- **Function-to-function flow** — every step that does non-trivial work carries `--fn <declared-fn>`. The sequence of pills along the steps reveals which function is active at each point in the flow.
|
|
76
|
+
- **Variable state transitions during the run** — every variable that is read or mutated by a step is declared via `variable add`, and that step lists it in `--reads` (the value flows in) or `--writes` (the value is created/updated). Reading the steps top-to-bottom traces each variable's life cycle.
|
|
77
|
+
- **Local data flow** — the ordered step boxes (with reads/writes chips) read as a directed flowchart, ending at the sub-module boundary (returning a value, persisting a row, emitting an event).
|
|
78
|
+
|
|
79
|
+
`apltk architecture validate` MUST return OK before reporting completion — it enforces both criteria's structural pieces (unknown sub-modules on edges, unknown functions/variables referenced by dataflow steps).
|
|
80
|
+
|
|
54
81
|
## How to use `apltk architecture`
|
|
55
82
|
|
|
56
83
|
Each verb is a single mutation; the CLI auto-renders after every change. Pass `--no-render` when batching mutations. Global flags: `--project <root>`, `--spec <spec_dir>` (writes to overlay; see `spec-to-project-html`), `--no-render`, `--no-open`.
|
|
@@ -63,7 +90,7 @@ Each verb is a single mutation; the CLI auto-renders after every change. Pass `-
|
|
|
63
90
|
| `apltk architecture submodule add --feature <slug> --slug <kebab> --kind ui\|api\|service\|db\|pure-fn\|queue\|external --role "..."` | Declare a sub-module under a feature. |
|
|
64
91
|
| `apltk architecture function add --feature X --submodule Y --name fn --in "T1, T2" --out "R \| ErrX" --side pure\|io\|write\|tx\|lock\|network --purpose "..."` | Add a function I/O row. |
|
|
65
92
|
| `apltk architecture variable add --feature X --submodule Y --name v --type T --scope call\|tx\|persist\|instance\|loop --purpose "..."` | Add a variable-with-business-purpose row. |
|
|
66
|
-
| `apltk architecture dataflow add --feature X --submodule Y --step "..." [--at N]` | Append (or insert at index `N`) an internal dataflow step. |
|
|
93
|
+
| `apltk architecture dataflow add --feature X --submodule Y --step "..." [--fn <declared-fn>] [--reads "v1,v2"] [--writes "v3,v4"] [--at N]` | Append (or insert at index `N`) an internal dataflow step. `--fn` must match a previously declared `function`; every name in `--reads` / `--writes` must match a previously declared `variable` in the same sub-module — the renderer surfaces them as a function pill + reads/writes chips so the diagram shows function-to-function flow and variable state transitions. |
|
|
67
94
|
| `apltk architecture dataflow reorder --feature X --submodule Y --from i --to j` | Move a step within the same sub-module. |
|
|
68
95
|
| `apltk architecture error add --feature X --submodule Y --name ErrCode --when "..." --means "..."` | Add a local error row. |
|
|
69
96
|
| `apltk architecture edge add --from <feature>[/sub] --to <feature>[/sub] --kind call\|return\|data-row\|failure --label "..." [--id <stable>]` | Add an edge. Intra-feature edges (both endpoints in the same feature with sub-modules) land in the feature YAML; cross-feature edges land in `atlas.index.yaml`. |
|
|
@@ -86,25 +113,32 @@ Scan the shipped source for **user-visible capabilities** (each one = one featur
|
|
|
86
113
|
|
|
87
114
|
### 2) Branch the deep-read by environment (Rule 3)
|
|
88
115
|
|
|
89
|
-
#### 2A) With subagents (preferred)
|
|
116
|
+
#### 2A) With subagents (preferred) — workers own their feature; main agent owns the macro seams
|
|
90
117
|
|
|
91
|
-
Dispatch one
|
|
118
|
+
Dispatch one **write-capable** subagent per feature, plus the main agent for the macro layer. Hand each subagent: its feature slug, the feature-module list from step 1 (so it knows other features' slugs for outbound edges), and access to `apltk architecture`. Each subagent **declares its own feature end-to-end** and only reports outbound boundaries upward:
|
|
92
119
|
|
|
93
|
-
> **Feature `<slug>`
|
|
94
|
-
> -
|
|
95
|
-
> -
|
|
96
|
-
> -
|
|
120
|
+
> **Feature `<slug>` subagent contract**
|
|
121
|
+
> - Read every sub-module of this feature.
|
|
122
|
+
> - Run `feature add --slug <slug> --title "..." --story "..." --no-render` (or `feature set` if a placeholder already exists).
|
|
123
|
+
> - For every sub-module: `submodule add` with the right `--kind`, then add its `function`, `variable`, `dataflow` (ordered — these steps carry the variable state transitions through the flow), and `error` rows.
|
|
124
|
+
> - For every intra-feature interaction between sub-modules — function calls, returns, error propagation, rollback / compensation — emit `edge add --from <slug>/<a> --to <slug>/<b> --kind call|return|data-row|failure --label "..."`. Failure / rollback flows belong here: model them as additional `--kind failure` edges or as ordered `dataflow add` steps on the affected sub-modules, whichever maps to the real code path.
|
|
125
|
+
> - Run `apltk architecture validate` (scoped to this feature) before returning.
|
|
126
|
+
> - **Return ONLY**: (i) sub-module list (slug + kind + one-line role), (ii) outbound boundaries to *other* features' sub-modules (direction + edge kind + suggested label). No source excerpts, no function bodies.
|
|
97
127
|
|
|
98
|
-
Main agent
|
|
128
|
+
Main agent — after every subagent returns — declares **only** macro-level state from the aggregated outbound boundaries:
|
|
99
129
|
|
|
100
130
|
```bash
|
|
101
131
|
apltk architecture meta set --title "..." --summary "..." --no-render
|
|
102
|
-
|
|
103
|
-
|
|
132
|
+
# only when an actor is shared across multiple features:
|
|
133
|
+
apltk architecture actor add --id <id> --label "..." --no-render
|
|
134
|
+
# one edge per cross-feature interaction reported by the subagents:
|
|
135
|
+
apltk architecture edge add --from <featA>/<subA> --to <featB>/<subB> --kind call|return|data-row|failure --label "..." --no-render
|
|
104
136
|
apltk architecture render
|
|
105
137
|
apltk architecture validate
|
|
106
138
|
```
|
|
107
139
|
|
|
140
|
+
The main agent **MUST NOT** re-declare a subagent's intra-feature components, and **MUST NOT** open source files for any feature it delegated.
|
|
141
|
+
|
|
108
142
|
#### 2B) Without subagents — feature-by-feature read-declare loop
|
|
109
143
|
|
|
110
144
|
Process the list from step 1 one feature at a time (topological hint: read-from data sources and pure helpers first, user-facing entries last). Per feature:
|
|
@@ -126,8 +160,10 @@ Report: feature count, sub-module count, macro edge counts (call / return / data
|
|
|
126
160
|
## Sample hints
|
|
127
161
|
|
|
128
162
|
- Multiple SQL paths on `service ↔ db` → call `edge add` once per SQL path so the macro shows them as separate edges.
|
|
129
|
-
- Retry loops between `service ↔ generator(pure)` → call/return pair in the macro plus a dataflow step in the service describing the retry budget.
|
|
163
|
+
- Retry loops between `service ↔ generator(pure)` → call/return pair in the macro plus a `dataflow add` step in the service with `--fn issueCode --writes "code"` describing the retry budget; a follow-up step `--fn issueCode --reads "code" --writes "row"` then captures the persistence attempt.
|
|
164
|
+
- Variable state transitions inside one function (e.g. `code` is computed, persisted, then returned) → emit one `dataflow add` per logical mutation, with `--fn` constant and `--reads` / `--writes` listing every variable that changes shape at that point. The sub-module page chips trace each variable's life cycle top-to-bottom.
|
|
130
165
|
- Cross-feature DB hand-off (A writes, B reads) → declare both sides' `INSERT_*` / `SELECT_*` functions on the `db` sub-module, then `edge add --kind data-row` from producer feature/submodule to consumer feature/submodule.
|
|
166
|
+
- Rollback / compensation across sub-modules → `edge add --kind failure --label "rollback on dup"`; rollback *within* a single sub-module belongs in `dataflow add` (with `--fn` set to whichever function owns the cleanup).
|
|
131
167
|
- Third-party systems → declare as `--kind external` sub-modules; the trust boundary becomes visible because the renderer styles them differently.
|
|
132
168
|
|
|
133
169
|
## References
|
|
@@ -3,11 +3,11 @@ interface:
|
|
|
3
3
|
short_description: "Declare the project HTML architecture atlas through `apltk architecture` CLI verbs"
|
|
4
4
|
default_prompt: >-
|
|
5
5
|
Use $init-project-html. Its `SKILL.md` is the authoritative rulebook; `references/TEMPLATE_SPEC.md` is a component-schema cheat sheet.
|
|
6
|
-
NEVER hand-author files under `resources/project-architecture/**/*.html`. The atlas state lives in YAML under `resources/project-architecture/atlas/` and every mutation goes through `apltk architecture <verb> ...`; the CLI owns layout, no-overlap, DOM, CSS, ARIA, and pan/zoom.
|
|
7
|
-
Read strategy to avoid context loss
|
|
6
|
+
NEVER hand-author files under `resources/project-architecture/**/*.html`. The atlas state lives in YAML under `resources/project-architecture/atlas/` and every mutation goes through `apltk architecture <verb> ...`; the CLI owns layout, no-overlap, DOM, CSS, ARIA, and pan/zoom — both the macro SVG and each sub-module's internal-dataflow diagram are zoomable.
|
|
7
|
+
Read strategy to avoid context loss AND to enforce a hard responsibility split. STEP 1: enumerate the feature-module list (slug + entry + boundary resources) WITHOUT diving into function bodies.
|
|
8
8
|
STEP 2: branch by environment.
|
|
9
|
-
(2A, preferred) if subagents are available, dispatch ONE
|
|
10
|
-
(2B, no subagents) process features ONE AT A TIME — deep-read feature A, IMMEDIATELY drive the CLI (`feature add`, `submodule add` x N, `function add` / `variable add` / `dataflow add` / `error add` rows, intra-feature `edge add`), drop A's function-level details from working memory, then move to feature B. For cross-feature edges pointing at a not-yet-declared feature, declare a placeholder with `feature add --slug <future> --title <future>` first and refine its title/story on a later pass.
|
|
9
|
+
(2A, preferred) if subagents are available, dispatch ONE write-capable subagent per feature. Each subagent deep-reads ITS OWN feature and declares EVERY intra-feature interaction itself via `apltk architecture ... --feature <slug>`: `submodule add` (with the right `--kind`), `function add` / `variable add` / `dataflow add` (ordered — these steps carry the variable state transitions through the flow) / `error add` rows on every sub-module, and **every intra-feature edge** between its sub-modules (function calls, returns, error propagation, rollback / compensation — model failure / rollback paths as `--kind failure` edges and/or as ordered `dataflow add` steps). The subagent returns ONLY (i) sub-module list (slug + kind + one-line role) and (ii) outbound boundaries to other features' sub-modules (direction + edge kind + suggested label). The main agent never re-reads source for a delegated feature and never re-declares its components — it batches ONLY cross-feature `edge add` (and any shared `actor` / `meta`) from the aggregated outbound summaries, then runs `apltk architecture render` and `apltk architecture validate`.
|
|
10
|
+
(2B, no subagents) process features ONE AT A TIME — deep-read feature A, IMMEDIATELY drive the CLI (`feature add`, `submodule add` x N, `function add` / `variable add` / `dataflow add` / `error add` rows, intra-feature `edge add` including failure / rollback edges), drop A's function-level details from working memory, then move to feature B. For cross-feature edges pointing at a not-yet-declared feature, declare a placeholder with `feature add --slug <future> --title <future>` first and refine its title/story on a later pass.
|
|
11
11
|
CLI verbs to declare components (always kebab-case slugs; pass `--no-render` to batch then call `apltk architecture render` once at the end):
|
|
12
12
|
`apltk architecture meta set --title "..." --summary "..."`,
|
|
13
13
|
`apltk architecture actor add --id <kebab> --label "..."`,
|
|
@@ -15,8 +15,8 @@ interface:
|
|
|
15
15
|
`apltk architecture submodule add --feature X --slug Y --kind ui|api|service|db|pure-fn|queue|external --role "..."`,
|
|
16
16
|
`apltk architecture function add --feature X --submodule Y --name fn --in "T1, T2" --out "R | ErrX" --side pure|io|write|tx|lock|network --purpose "..."`,
|
|
17
17
|
`apltk architecture variable add --feature X --submodule Y --name v --type T --scope call|tx|persist|instance|loop --purpose "..."`,
|
|
18
|
-
`apltk architecture dataflow add --feature X --submodule Y --step "..." [--at N]
|
|
18
|
+
`apltk architecture dataflow add --feature X --submodule Y --step "..." [--fn <declared-fn>] [--reads "v1,v2"] [--writes "v3,v4"] [--at N]` (use `--fn` to surface function-to-function flow inside the sub-module; use `--reads` / `--writes` to surface variable state transitions — every name MUST match a `function`/`variable` already declared in the SAME sub-module or `validate` fails),
|
|
19
19
|
`apltk architecture error add --feature X --submodule Y --name ErrCode --when "..." --means "..."`,
|
|
20
20
|
`apltk architecture edge add --from <feature>[/sub] --to <feature>[/sub] --kind call|return|data-row|failure --label "..." [--id <stable>]`.
|
|
21
21
|
Intra-feature edges (both endpoints in the same feature with sub-modules) land in the feature YAML; cross-feature edges land in `atlas.index.yaml`. After the last mutation run `apltk architecture validate` — must return OK; resolve every reported error before reporting completion.
|
|
22
|
-
Each sub-module page describes ONLY itself (`sub-io` function I/O + `sub-vars` variables-with-business-purpose + `sub-dataflow` internal flow + `sub-errors` local errors) — express any cross-boundary interaction as
|
|
22
|
+
Each sub-module page describes ONLY itself (`sub-io` function I/O + `sub-vars` variables-with-business-purpose + `sub-dataflow` zoomable internal flow with fn pill + reads/writes chips + `sub-errors` local errors) — express any cross-boundary interaction as an edge, never as sub-module page prose. Two acceptance criteria gate completion: (1) the macro diagram clearly shows feature×sub-module relationships — data flow, call/return interaction logic, error handling, rollback — all as `--kind call|return|data-row|failure` edges (no cross-boundary path expressed only as prose); (2) every non-trivial sub-module's internal diagram clearly shows function-level interactions — `dataflow add --fn <declared-fn>` for function-to-function flow + `--reads` / `--writes` for variable state transitions through the run. Anchor every declaration to a concrete path / symbol / SQL / config; mark TBD when evidence is missing; record scanned roots and deliberate omissions in `meta.summary`. Report which read strategy (2A or 2B) was actually used and the location of the rendered atlas (`resources/project-architecture/index.html`).
|
|
@@ -74,11 +74,17 @@ p { line-height: 1.55; color: var(--text); }
|
|
|
74
74
|
/* ---- SVG macro ---- */
|
|
75
75
|
.m-cluster__bg { fill: rgba(15, 23, 42, 0.55); stroke: var(--border); stroke-width: 1; }
|
|
76
76
|
.m-cluster__title { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 14px; fill: var(--text); font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
|
|
77
|
-
.m-node
|
|
78
|
-
.m-node
|
|
77
|
+
.m-node { cursor: pointer; }
|
|
78
|
+
.m-node rect { fill: var(--panel-soft); stroke: var(--border); stroke-width: 1; transition: stroke 120ms ease, fill 120ms ease; }
|
|
79
|
+
.m-node:hover rect,
|
|
80
|
+
.m-node:focus rect,
|
|
81
|
+
.m-node:focus-visible rect { stroke: var(--accent); stroke-width: 1.6; fill: rgba(56, 189, 248, 0.08); }
|
|
82
|
+
.m-node:focus { outline: none; }
|
|
79
83
|
.m-node__title { font-size: 13px; font-weight: 600; fill: var(--text); }
|
|
80
84
|
.m-node__kind { font-size: 11px; fill: var(--muted); }
|
|
81
85
|
.m-node__role { font-size: 11px; fill: var(--muted); }
|
|
86
|
+
.m-node:hover .m-node__title,
|
|
87
|
+
.m-node:focus .m-node__title { fill: var(--accent); }
|
|
82
88
|
|
|
83
89
|
.m-node--ui rect { stroke: var(--kind-ui); }
|
|
84
90
|
.m-node--api rect { stroke: var(--kind-api); }
|
|
@@ -133,8 +139,23 @@ p { line-height: 1.55; color: var(--text); }
|
|
|
133
139
|
|
|
134
140
|
.sub-section__empty { color: var(--muted); font-style: italic; font-size: 13px; }
|
|
135
141
|
|
|
136
|
-
.sub-
|
|
137
|
-
.sub-
|
|
138
|
-
.sub-
|
|
139
|
-
.sub-
|
|
142
|
+
.sub-dataflow__canvas { position: relative; background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 16px; }
|
|
143
|
+
.sub-dataflow__toolbar { position: absolute; top: 16px; right: 16px; display: flex; gap: 4px; z-index: 2; }
|
|
144
|
+
.sub-dataflow__toolbar button { background: var(--panel-soft); color: var(--text); border: 1px solid var(--border); padding: 4px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; }
|
|
145
|
+
.sub-dataflow__toolbar button:hover { border-color: var(--accent); color: var(--accent); }
|
|
146
|
+
.sub-dataflow__viewport { width: 100%; max-height: 60vh; overflow: hidden; border-radius: 8px; background: #0b1220; }
|
|
147
|
+
.sub-dataflow__viewport.is-grabbing { cursor: grabbing; }
|
|
148
|
+
.sub-dataflow__viewport:not(.is-grabbing) { cursor: grab; }
|
|
149
|
+
.sub-dataflow__svg { width: 100%; height: auto; max-height: 60vh; display: block; user-select: none; touch-action: none; }
|
|
150
|
+
.sub-dataflow__badge { fill: var(--panel-soft); stroke: var(--accent); stroke-width: 1.4; }
|
|
151
|
+
.sub-dataflow__badge-text { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 13px; font-weight: 600; fill: var(--accent); }
|
|
152
|
+
.sub-dataflow__step .sub-dataflow__box { fill: var(--panel-soft); stroke: var(--border); stroke-width: 1; }
|
|
153
|
+
.sub-dataflow__step:hover .sub-dataflow__box { stroke: var(--accent); }
|
|
154
|
+
.sub-dataflow__text { font-family: ui-sans-serif, system-ui, sans-serif; font-size: 14px; fill: var(--text); }
|
|
155
|
+
.sub-dataflow__arrow { stroke: var(--muted); stroke-width: 1.6; }
|
|
156
|
+
.sub-dataflow__fn-bg { fill: rgba(56, 189, 248, 0.12); stroke: var(--accent); stroke-width: 1; }
|
|
157
|
+
.sub-dataflow__fn-text { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; font-weight: 600; fill: var(--accent); letter-spacing: 0.02em; }
|
|
158
|
+
.sub-dataflow__chip { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; font-weight: 500; }
|
|
159
|
+
.sub-dataflow__chip--reads { fill: var(--kind-service); }
|
|
160
|
+
.sub-dataflow__chip--writes { fill: var(--kind-db); }
|
|
140
161
|
.sub-dataflow__empty { color: var(--muted); font-style: italic; }
|
|
@@ -1,93 +1,136 @@
|
|
|
1
|
-
/* viewer.client.js — pan/zoom for
|
|
2
|
-
*
|
|
3
|
-
* [data-
|
|
4
|
-
*
|
|
1
|
+
/* viewer.client.js — pan/zoom for every atlas SVG on the page.
|
|
2
|
+
*
|
|
3
|
+
* Each `[data-pan-zoom-viewport]` element gets its own state and
|
|
4
|
+
* handlers, so a single page can host the macro atlas SVG AND
|
|
5
|
+
* sub-module-internal dataflow SVGs simultaneously. Toolbar buttons
|
|
6
|
+
* (`[data-pan-zoom="zoom-in|zoom-out|fit"]`) are scoped to the
|
|
7
|
+
* containing `[data-pan-zoom-container]` (falls back to the viewport's
|
|
8
|
+
* direct parent), so multiple toolbars on one page do not collide.
|
|
9
|
+
* Keyboard shortcuts (`←` / `→` / `↑` / `↓` / `+` / `−` / `0`) drive
|
|
10
|
+
* the page's first viewport — the "primary" diagram of that page
|
|
11
|
+
* (macro SVG on `index.html`; sub-dataflow SVG on a sub-module page).
|
|
12
|
+
*/
|
|
5
13
|
|
|
6
14
|
(function () {
|
|
7
15
|
'use strict';
|
|
8
16
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (!svg) return;
|
|
17
|
+
const viewports = Array.from(document.querySelectorAll('[data-pan-zoom-viewport]'));
|
|
18
|
+
const controllers = viewports.map(setupViewport).filter(Boolean);
|
|
19
|
+
if (controllers.length === 0) return;
|
|
13
20
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
const primary = controllers[0];
|
|
22
|
+
document.addEventListener('keydown', function (evt) {
|
|
23
|
+
if (evt.target && (evt.target.tagName === 'INPUT' || evt.target.tagName === 'TEXTAREA')) return;
|
|
24
|
+
if (evt.key === 'ArrowLeft') { primary.pan(-1, 0); }
|
|
25
|
+
else if (evt.key === 'ArrowRight') { primary.pan(1, 0); }
|
|
26
|
+
else if (evt.key === 'ArrowUp') { primary.pan(0, -1); }
|
|
27
|
+
else if (evt.key === 'ArrowDown') { primary.pan(0, 1); }
|
|
28
|
+
else if (evt.key === '+' || evt.key === '=') { primary.zoom(1 / 1.2); }
|
|
29
|
+
else if (evt.key === '-' || evt.key === '_') { primary.zoom(1.2); }
|
|
30
|
+
else if (evt.key === '0') { primary.fit(); }
|
|
31
|
+
});
|
|
22
32
|
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
state
|
|
30
|
-
state.h = newH;
|
|
31
|
-
apply();
|
|
32
|
-
}
|
|
33
|
+
function setupViewport(viewport) {
|
|
34
|
+
const svg = viewport.querySelector('[data-atlas-svg]');
|
|
35
|
+
if (!svg) return null;
|
|
36
|
+
const initial = svg.getAttribute('viewBox');
|
|
37
|
+
if (!initial) return null;
|
|
38
|
+
const [ix, iy, iw, ih] = initial.split(/\s+/).map(Number);
|
|
39
|
+
const state = { x: ix, y: iy, w: iw, h: ih };
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
function apply() {
|
|
42
|
+
svg.setAttribute('viewBox', `${state.x} ${state.y} ${state.w} ${state.h}`);
|
|
43
|
+
}
|
|
44
|
+
function fit() {
|
|
45
|
+
state.x = ix; state.y = iy; state.w = iw; state.h = ih;
|
|
46
|
+
apply();
|
|
47
|
+
}
|
|
48
|
+
function zoom(factor, cx, cy) {
|
|
49
|
+
const newW = Math.max(40, Math.min(state.w * factor, iw * 8));
|
|
50
|
+
const newH = newW * (state.h / state.w);
|
|
51
|
+
if (cx == null) { cx = state.x + state.w / 2; cy = state.y + state.h / 2; }
|
|
52
|
+
state.x = cx - (cx - state.x) * (newW / state.w);
|
|
53
|
+
state.y = cy - (cy - state.y) * (newH / state.h);
|
|
54
|
+
state.w = newW;
|
|
55
|
+
state.h = newH;
|
|
56
|
+
apply();
|
|
57
|
+
}
|
|
58
|
+
function pan(dirX, dirY) {
|
|
59
|
+
const stepX = state.w * 0.08;
|
|
60
|
+
const stepY = state.h * 0.08;
|
|
61
|
+
state.x += dirX * stepX;
|
|
62
|
+
state.y += dirY * stepY;
|
|
63
|
+
apply();
|
|
64
|
+
}
|
|
65
|
+
function clientToSvg(evt) {
|
|
66
|
+
const rect = svg.getBoundingClientRect();
|
|
67
|
+
const xRatio = (evt.clientX - rect.left) / rect.width;
|
|
68
|
+
const yRatio = (evt.clientY - rect.top) / rect.height;
|
|
69
|
+
return { x: state.x + xRatio * state.w, y: state.y + yRatio * state.h };
|
|
70
|
+
}
|
|
40
71
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
viewport.addEventListener('wheel', function (evt) {
|
|
73
|
+
if (!evt.ctrlKey && !evt.metaKey && Math.abs(evt.deltaY) < 4 && Math.abs(evt.deltaX) < 4) return;
|
|
74
|
+
evt.preventDefault();
|
|
75
|
+
const factor = evt.deltaY > 0 ? 1.1 : 1 / 1.1;
|
|
76
|
+
const pt = clientToSvg(evt);
|
|
77
|
+
zoom(factor, pt.x, pt.y);
|
|
78
|
+
}, { passive: false });
|
|
48
79
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
80
|
+
// Drag-pan: defer the pointer capture (and the "is-grabbing" class)
|
|
81
|
+
// until the pointer actually moves past a small threshold. Without
|
|
82
|
+
// this, a single click on an SVG <a> (sub-module link) would be
|
|
83
|
+
// captured by the viewport and never reach the link.
|
|
84
|
+
const DRAG_THRESHOLD_PX = 4;
|
|
85
|
+
let pending = null;
|
|
86
|
+
let dragging = null;
|
|
87
|
+
viewport.addEventListener('pointerdown', function (evt) {
|
|
88
|
+
if (evt.button !== 0) return;
|
|
89
|
+
pending = { x: evt.clientX, y: evt.clientY, pointerId: evt.pointerId };
|
|
90
|
+
});
|
|
91
|
+
viewport.addEventListener('pointermove', function (evt) {
|
|
92
|
+
if (!dragging && pending && pending.pointerId === evt.pointerId) {
|
|
93
|
+
const dx = evt.clientX - pending.x;
|
|
94
|
+
const dy = evt.clientY - pending.y;
|
|
95
|
+
if (Math.hypot(dx, dy) < DRAG_THRESHOLD_PX) return;
|
|
96
|
+
dragging = { x: pending.x, y: pending.y, pointerId: pending.pointerId };
|
|
97
|
+
pending = null;
|
|
98
|
+
viewport.classList.add('is-grabbing');
|
|
99
|
+
try { viewport.setPointerCapture(dragging.pointerId); } catch (e) { /* ignore */ }
|
|
100
|
+
}
|
|
101
|
+
if (!dragging || dragging.pointerId !== evt.pointerId) return;
|
|
102
|
+
evt.preventDefault();
|
|
103
|
+
const rect = svg.getBoundingClientRect();
|
|
104
|
+
const dx = ((evt.clientX - dragging.x) / rect.width) * state.w;
|
|
105
|
+
const dy = ((evt.clientY - dragging.y) / rect.height) * state.h;
|
|
106
|
+
state.x -= dx;
|
|
107
|
+
state.y -= dy;
|
|
108
|
+
dragging.x = evt.clientX;
|
|
109
|
+
dragging.y = evt.clientY;
|
|
110
|
+
apply();
|
|
111
|
+
});
|
|
112
|
+
function endDrag(evt) {
|
|
113
|
+
pending = null;
|
|
114
|
+
if (!dragging) return;
|
|
115
|
+
const draggedId = dragging.pointerId;
|
|
116
|
+
dragging = null;
|
|
117
|
+
viewport.classList.remove('is-grabbing');
|
|
118
|
+
try { viewport.releasePointerCapture(draggedId); } catch (e) { /* ignore */ }
|
|
119
|
+
// Suppress the synthetic click that would follow a drag-release
|
|
120
|
+
// on top of a sub-module <a>; only the no-movement case should
|
|
121
|
+
// navigate.
|
|
122
|
+
const swallow = (e) => { e.preventDefault(); e.stopPropagation(); };
|
|
123
|
+
viewport.addEventListener('click', swallow, { capture: true, once: true });
|
|
124
|
+
}
|
|
125
|
+
viewport.addEventListener('pointerup', endDrag);
|
|
126
|
+
viewport.addEventListener('pointercancel', endDrag);
|
|
127
|
+
viewport.addEventListener('pointerleave', endDrag);
|
|
75
128
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
else if (evt.key === 'ArrowRight') { state.x += step; apply(); }
|
|
81
|
-
else if (evt.key === 'ArrowUp') { state.y -= step; apply(); }
|
|
82
|
-
else if (evt.key === 'ArrowDown') { state.y += step; apply(); }
|
|
83
|
-
else if (evt.key === '+' || evt.key === '=') { zoom(1 / 1.2); }
|
|
84
|
-
else if (evt.key === '-' || evt.key === '_') { zoom(1.2); }
|
|
85
|
-
else if (evt.key === '0') { state.x = ix; state.y = iy; state.w = iw; state.h = ih; apply(); }
|
|
86
|
-
});
|
|
129
|
+
const container = viewport.closest('[data-pan-zoom-container]') || viewport.parentElement || document;
|
|
130
|
+
container.querySelectorAll('[data-pan-zoom="zoom-in"]').forEach((btn) => btn.addEventListener('click', () => zoom(1 / 1.2)));
|
|
131
|
+
container.querySelectorAll('[data-pan-zoom="zoom-out"]').forEach((btn) => btn.addEventListener('click', () => zoom(1.2)));
|
|
132
|
+
container.querySelectorAll('[data-pan-zoom="fit"]').forEach((btn) => btn.addEventListener('click', fit));
|
|
87
133
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
document.querySelectorAll('[data-pan-zoom="fit"]').forEach((btn) => btn.addEventListener('click', () => {
|
|
91
|
-
state.x = ix; state.y = iy; state.w = iw; state.h = ih; apply();
|
|
92
|
-
}));
|
|
134
|
+
return { zoom, fit, pan };
|
|
135
|
+
}
|
|
93
136
|
})();
|