mustflow 2.32.0 → 2.37.1
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/dist/cli/commands/api/actions.js +55 -0
- package/dist/cli/commands/api/report-runner.js +62 -0
- package/dist/cli/commands/api/serve.js +149 -0
- package/dist/cli/commands/api/workspace-recommendations.js +13 -0
- package/dist/cli/commands/api.js +15 -275
- package/dist/cli/lib/local-index/search-read-model.js +44 -7
- package/dist/cli/lib/validation/frontmatter.js +75 -0
- package/dist/cli/lib/validation/index.js +4 -86
- package/dist/cli/lib/validation/safe-read.js +13 -0
- package/dist/core/active-run-locks.js +110 -10
- package/dist/core/run-performance-history.js +14 -1
- package/dist/core/validation-ratchet.js +1 -1
- package/package.json +1 -1
- package/templates/default/i18n.toml +55 -19
- package/templates/default/locales/en/.mustflow/docs/agent-workflow.md +15 -7
- package/templates/default/locales/en/.mustflow/skills/INDEX.md +20 -8
- package/templates/default/locales/en/.mustflow/skills/adapter-boundary/SKILL.md +9 -2
- package/templates/default/locales/en/.mustflow/skills/ai-generated-code-hardening/SKILL.md +249 -0
- package/templates/default/locales/en/.mustflow/skills/api-contract-change/SKILL.md +16 -11
- package/templates/default/locales/en/.mustflow/skills/auth-permission-change/SKILL.md +11 -4
- package/templates/default/locales/en/.mustflow/skills/backend-reliability-change/SKILL.md +289 -0
- package/templates/default/locales/en/.mustflow/skills/css-code-change/SKILL.md +24 -14
- package/templates/default/locales/en/.mustflow/skills/dependency-upgrade-review/SKILL.md +18 -7
- package/templates/default/locales/en/.mustflow/skills/frontend-render-stability/SKILL.md +144 -0
- package/templates/default/locales/en/.mustflow/skills/go-code-change/SKILL.md +70 -18
- package/templates/default/locales/en/.mustflow/skills/html-code-change/SKILL.md +20 -13
- package/templates/default/locales/en/.mustflow/skills/http-delivery-streaming/SKILL.md +205 -0
- package/templates/default/locales/en/.mustflow/skills/performance-budget-check/SKILL.md +9 -7
- package/templates/default/locales/en/.mustflow/skills/proactive-risk-surfacing/SKILL.md +198 -0
- package/templates/default/locales/en/.mustflow/skills/python-code-change/SKILL.md +27 -11
- package/templates/default/locales/en/.mustflow/skills/routes.toml +37 -1
- package/templates/default/locales/en/.mustflow/skills/rust-code-change/SKILL.md +41 -17
- package/templates/default/locales/en/.mustflow/skills/service-boundary-architecture/SKILL.md +167 -0
- package/templates/default/locales/en/.mustflow/skills/tailwind-code-change/SKILL.md +37 -23
- package/templates/default/locales/en/.mustflow/skills/tauri-code-change/SKILL.md +27 -10
- package/templates/default/locales/en/.mustflow/skills/typescript-code-change/SKILL.md +22 -4
- package/templates/default/locales/en/.mustflow/skills/unocss-code-change/SKILL.md +34 -15
- package/templates/default/locales/en/.mustflow/skills/version-freshness-check/SKILL.md +29 -5
- package/templates/default/locales/en/AGENTS.md +3 -2
- package/templates/default/locales/ko/.mustflow/docs/agent-workflow.md +13 -8
- package/templates/default/locales/ko/AGENTS.md +2 -2
- package/templates/default/manifest.toml +37 -1
|
@@ -12,6 +12,12 @@ route_type = "primary"
|
|
|
12
12
|
priority = 70
|
|
13
13
|
applies_to_reasons = ["code_change", "cross_cutting_code_change"]
|
|
14
14
|
|
|
15
|
+
[routes."service-boundary-architecture"]
|
|
16
|
+
category = "architecture_patterns"
|
|
17
|
+
route_type = "primary"
|
|
18
|
+
priority = 82
|
|
19
|
+
applies_to_reasons = ["code_change", "behavior_change", "data_change", "migration_change", "performance_change", "security_change", "privacy_change", "public_api_change"]
|
|
20
|
+
|
|
15
21
|
[routes."code-review"]
|
|
16
22
|
category = "general_code"
|
|
17
23
|
route_type = "primary"
|
|
@@ -72,6 +78,12 @@ route_type = "adjunct"
|
|
|
72
78
|
priority = 85
|
|
73
79
|
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "public_api_change", "test_change", "docs_change", "mustflow_docs_change", "mustflow_config_change", "package_metadata_change", "release_risk"]
|
|
74
80
|
|
|
81
|
+
[routes."proactive-risk-surfacing"]
|
|
82
|
+
category = "workflow_contracts"
|
|
83
|
+
route_type = "event"
|
|
84
|
+
priority = 72
|
|
85
|
+
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "test_change", "docs_change", "mustflow_docs_change", "public_api_change", "security_change", "privacy_change", "data_change", "performance_change", "ui_change", "release_risk"]
|
|
86
|
+
|
|
75
87
|
[routes."restricted-handoff-resume"]
|
|
76
88
|
category = "workflow_contracts"
|
|
77
89
|
route_type = "primary"
|
|
@@ -138,6 +150,12 @@ route_type = "primary"
|
|
|
138
150
|
priority = 55
|
|
139
151
|
applies_to_reasons = ["unknown_change", "code_change", "docs_change", "test_change", "behavior_change", "performance_change", "ui_change", "data_change"]
|
|
140
152
|
|
|
153
|
+
[routes."ai-generated-code-hardening"]
|
|
154
|
+
category = "general_code"
|
|
155
|
+
route_type = "adjunct"
|
|
156
|
+
priority = 74
|
|
157
|
+
applies_to_reasons = ["unknown_change", "code_change", "behavior_change", "test_change", "public_api_change", "performance_change"]
|
|
158
|
+
|
|
141
159
|
[routes."clarifying-question-gate"]
|
|
142
160
|
category = "general_code"
|
|
143
161
|
route_type = "adjunct"
|
|
@@ -186,11 +204,23 @@ route_type = "primary"
|
|
|
186
204
|
priority = 82
|
|
187
205
|
applies_to_reasons = ["code_change", "public_api_change", "docs_change", "test_change"]
|
|
188
206
|
|
|
207
|
+
[routes."backend-reliability-change"]
|
|
208
|
+
category = "general_code"
|
|
209
|
+
route_type = "primary"
|
|
210
|
+
priority = 84
|
|
211
|
+
applies_to_reasons = ["code_change", "behavior_change", "public_api_change", "data_change", "migration_change", "performance_change", "security_change", "privacy_change", "docs_change", "test_change", "package_metadata_change", "release_risk"]
|
|
212
|
+
|
|
213
|
+
[routes."http-delivery-streaming"]
|
|
214
|
+
category = "general_code"
|
|
215
|
+
route_type = "primary"
|
|
216
|
+
priority = 84
|
|
217
|
+
applies_to_reasons = ["code_change", "behavior_change", "public_api_change", "performance_change", "security_change", "privacy_change", "docs_change", "test_change", "package_metadata_change", "release_risk"]
|
|
218
|
+
|
|
189
219
|
[routes."typescript-code-change"]
|
|
190
220
|
category = "general_code"
|
|
191
221
|
route_type = "primary"
|
|
192
222
|
priority = 85
|
|
193
|
-
applies_to_reasons = ["code_change", "public_api_change", "test_change"]
|
|
223
|
+
applies_to_reasons = ["code_change", "public_api_change", "test_change", "package_metadata_change"]
|
|
194
224
|
|
|
195
225
|
[routes."javascript-code-change"]
|
|
196
226
|
category = "general_code"
|
|
@@ -468,6 +498,12 @@ route_type = "adjunct"
|
|
|
468
498
|
priority = 60
|
|
469
499
|
applies_to_reasons = ["migration_change"]
|
|
470
500
|
|
|
501
|
+
[routes."frontend-render-stability"]
|
|
502
|
+
category = "ui_assets"
|
|
503
|
+
route_type = "primary"
|
|
504
|
+
priority = 78
|
|
505
|
+
applies_to_reasons = ["ui_change", "performance_change", "behavior_change", "code_change"]
|
|
506
|
+
|
|
471
507
|
[routes."ui-quality-gate"]
|
|
472
508
|
category = "ui_assets"
|
|
473
509
|
route_type = "primary"
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
mustflow_doc: skill.rust-code-change
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 4
|
|
6
6
|
lifecycle: mustflow-owned
|
|
7
7
|
authority: procedure
|
|
8
8
|
name: rust-code-change
|
|
9
|
-
description: Apply this skill when Rust source, Cargo metadata, features, traits, errors, ownership, async runtime, unsafe code, tests, examples, or public crate APIs are created or changed.
|
|
9
|
+
description: Apply this skill when Rust source, Cargo metadata, features, traits, errors, ownership, async runtime, unsafe code, tests, examples, benchmarks, release profiles, MSRV, toolchain declarations, standard-library APIs, or public crate APIs are created or changed.
|
|
10
10
|
metadata:
|
|
11
11
|
mustflow_schema: "1"
|
|
12
12
|
mustflow_kind: procedure
|
|
@@ -40,6 +40,7 @@ instead of treated as incidental.
|
|
|
40
40
|
|
|
41
41
|
- `.rs`, `Cargo.toml`, `Cargo.lock`, workspace config, feature flags, public exports, traits, error types, tests, examples, benches, FFI, async runtime, or unsafe code change.
|
|
42
42
|
- The task touches ownership, borrowing, lifetimes, `Clone`, `Arc`, `Mutex`, `unwrap`, or public crate compatibility.
|
|
43
|
+
- The task introduces or reviews Rust-version-gated APIs or language behavior such as `let else`, let chains, match `if let` guards, `cfg_select!`, `assert_matches!`, `core::range`, `Vec::push_mut`, `HashMap::get_disjoint_mut`, `Option::take_if`, `LazyLock`, `OnceLock`, `workspace.lints`, `rust-version`, Rust 2024 lints, or release-profile tuning.
|
|
43
44
|
|
|
44
45
|
<!-- mustflow-section: do-not-use-when -->
|
|
45
46
|
## Do Not Use When
|
|
@@ -54,6 +55,7 @@ instead of treated as incidental.
|
|
|
54
55
|
- Relevant `src/lib.rs`, `src/main.rs`, modules, public re-exports, tests, examples, and docs examples.
|
|
55
56
|
- Existing error handling convention and async runtime.
|
|
56
57
|
- Public crate status, minimum supported Rust version, feature support policy, and downstream compatibility expectations when available.
|
|
58
|
+
- `rust-version`, edition, `rust-toolchain.toml`, CI toolchain matrix, target triples, Cargo resolver, workspace inheritance policy, and whether newer standard-library APIs require a raised MSRV.
|
|
57
59
|
- Host and build-loop constraints: OS, shell, native toolchain prerequisites, VM or remote-builder use, release profile, LTO, workspace size, disk budget, and configured smoke or focused-check intents.
|
|
58
60
|
- Configured verification intents.
|
|
59
61
|
|
|
@@ -66,6 +68,7 @@ instead of treated as incidental.
|
|
|
66
68
|
- Identify the intended edit-check-test loop before choosing a broad build. Treat whole-workspace
|
|
67
69
|
checks, release builds, fat LTO, cross-compiles, and sanitizer-style runs as expensive evidence
|
|
68
70
|
unless the command contract declares them as the normal focused path.
|
|
71
|
+
- If a Rust release, "latest stable Rust", standard-library feature, Cargo behavior, edition behavior, lint default, or toolchain-support claim is written durably, use `version-freshness-check` and official Rust sources before relying on memory or pasted notes.
|
|
69
72
|
|
|
70
73
|
<!-- mustflow-section: allowed-edits -->
|
|
71
74
|
## Allowed Edits
|
|
@@ -93,20 +96,35 @@ instead of treated as incidental.
|
|
|
93
96
|
MSVC, SDK, linker, or environment setup is required;
|
|
94
97
|
- treat VM builds, remote builders, release profiles, fat LTO, and whole-workspace checks as
|
|
95
98
|
environment and verification choices that must be reported when they dominate iteration cost.
|
|
96
|
-
4.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
4. Determine the Rust version contract before using newer syntax or standard-library APIs:
|
|
100
|
+
- treat `rust-version`, edition, `rust-toolchain.toml`, CI matrix, docs.rs metadata, and downstream compatibility notes as the MSRV evidence ledger;
|
|
101
|
+
- do not use 1.95+ APIs such as `cfg_select!`, match `if let` guards, or `Vec::push_mut`, or 1.96+ APIs such as `assert_matches!` and `core::range`, unless the declared MSRV and toolchain path support them;
|
|
102
|
+
- keep experimental, nightly-only, target-specific, or edition-specific behavior behind explicit gates or fallbacks instead of calling it general Rust advice.
|
|
103
|
+
5. Prefer flatter control flow when the MSRV supports it: use `let else` for early validation, let chains for related optional/result guards, and match `if let` guards for state-machine refinements. Remember that guard patterns do not satisfy match exhaustiveness; keep the fallback arm meaningful.
|
|
104
|
+
6. In tests, prefer `assert_matches!` over `assert!(matches!(...))` when the MSRV supports it and the failed value has useful `Debug` output. Import it explicitly from `std` or `core`; do not assume it is in the prelude.
|
|
105
|
+
7. Resolve ownership problems in this order: identify the real owner, shrink borrow scopes, fix function signatures to accept references or slices when ownership is unnecessary, distinguish transfer from sharing, then consider clone or shared ownership only when the semantics require it.
|
|
106
|
+
8. Before adding `clone`, verify it is a cheap handle clone such as `Arc`, `Rc`, or `Bytes`, a small intentional value clone, or a true independent ownership split. Reject large collection clones, loop clones, clone-then-borrow code, and whole-state clones made only to satisfy `spawn`.
|
|
107
|
+
9. Before adding `Arc<Mutex<_>>`, verify multiple owners truly need shared mutable state. For read-mostly snapshots, prefer ownership-preserving choices such as `Arc::make_mut`, immutable swaps, or explicit reload boundaries. Keep critical sections short, document lock order when relevant, and do not hold a lock guard across `.await`, I/O, callbacks, or user code.
|
|
108
|
+
10. Choose initialization primitives by input and failure semantics: use `LazyLock` for no-argument static lazy values that may poison permanently on panic, and `OnceLock` when boot-time or test-time code supplies the value or panic poisoning must not become the recovery policy.
|
|
109
|
+
11. Avoid hidden allocation when cheaper type contracts fit: use `Cow<'_, str>` or borrowed slices for mostly-borrowed results, query `HashMap<String, V>` with `&str` when `Borrow` supports it, use `Option::take`, `take_if`, or `as_slice` for state transitions and 0-or-1 iteration, and use `ControlFlow`, `try_for_each`, or `try_fold` when visitor or iterator APIs need explicit short-circuiting.
|
|
110
|
+
12. Treat collection and string capacity as part of performance correctness. Use `with_capacity`, `reserve`, `spare_capacity_mut`, or `push_mut` only when the safety and MSRV contract are clear; keep `set_len` inside a small proven unsafe boundary; avoid repeated `String::insert` or front insertion loops that create quadratic movement.
|
|
111
|
+
13. Use explicit lifetimes only to describe real borrow relationships. Do not add `'static` or `T: 'static` to public APIs merely because an internal task boundary requires it.
|
|
112
|
+
14. Use concrete error enums for library APIs when callers need to classify failures. Keep `Box<dyn Error>` mostly to binaries, examples, tests, prototypes, or explicitly opaque error policies.
|
|
113
|
+
15. Avoid `unwrap`, vague `expect`, and unbounded `panic!` in production paths. They are allowed only for tests, examples, startup policy, panic-boundary adapters, or invariants already proven by nearby code.
|
|
114
|
+
16. Review public API shape before adding `impl Trait`, `Deref`, or trait/lifetime machinery:
|
|
115
|
+
- argument-position `impl Trait` removes caller turbofish control and can be a public breaking change when converted from named generics;
|
|
116
|
+
- return-position `impl Trait` hides one concrete type, so divergent iterator or future branches need an enum, boxed trait object, or different API boundary;
|
|
117
|
+
- implement `Deref` only for pointer-like wrappers, not domain inheritance or method forwarding;
|
|
118
|
+
- use GATs for borrowing iterator/view traits when they remove a real allocation or boxed lifetime escape, not as decorative complexity.
|
|
119
|
+
17. If feature flags or Cargo workspace metadata change, treat default features, no-default builds, all-features builds, optional dependency implicit features, resolver behavior, target-specific dependencies, `workspace.package`, `workspace.dependencies`, `workspace.lints`, public re-exports, docs examples, and feature-gated trait impls as compatibility surfaces. Features should be additive, and `resolver = "2"` or a newer resolver decision must match the crate's edition/MSRV policy.
|
|
120
|
+
18. Treat public re-exports, public dependency types, generic bounds, trait item sets, error enum variants, `#[non_exhaustive]`, and MSRV as public API. Tightened bounds, added required trait methods, removed re-exports, changed error variants, or raised `rust-version` require compatibility review.
|
|
121
|
+
19. Do not mix async runtimes. A Tokio crate should not casually gain `async-std` or runtime-specific APIs in library core. Do not call blocking I/O or CPU-heavy work in async paths without an established boundary such as async-native APIs, a blocking pool, or a dedicated worker.
|
|
122
|
+
20. For async spawning, avoid leaking internal `Send + Sync + 'static` requirements into public APIs. Prefer owned task state, smaller spawn boundaries, local task structures, or caller-owned runtime decisions.
|
|
123
|
+
21. Touch `unsafe` only when a safe design cannot express the required behavior. Every unsafe block needs a nearby `SAFETY:` explanation; every public `unsafe fn` needs `# Safety` docs. In Rust 2024 or when `unsafe_op_in_unsafe_fn` is enabled, unsafe operations inside `unsafe fn` still need explicit unsafe blocks. Keep unsafe scopes small and wrap them in safe abstractions only when callers have no hidden safety obligations.
|
|
124
|
+
22. For FFI, keep Rust ABI types out of C boundaries. Use explicit ownership, `#[repr(C)]` where required, raw pointer plus length pairs, `CStr`/`CString`, RAII wrappers, null handling, panic boundaries, and documented thread-safety evidence before manual `Send` or `Sync`.
|
|
125
|
+
23. Review release profiles when the task changes binary delivery, CLI startup, embedded, wasm, or performance behavior. Treat `opt-level`, LTO, `panic`, `codegen-units`, and `strip` as product tradeoffs that must be measured or reported, not decorative Cargo knobs.
|
|
126
|
+
24. Calibrate performance claims. Do not claim Rust made a system faster from compile success, empty-database timings, warm-cache microbenchmarks, local-only runs, or debug versus release confusion. Require representative data size, concurrency, target hardware, profile, and measurement method before reporting speed claims.
|
|
127
|
+
25. Choose configured verification intents that cover format, lint, build, tests, feature combinations, docs, public API, unsafe, FFI, smoke targets, package artifact, and release-profile risk when available.
|
|
110
128
|
|
|
111
129
|
<!-- mustflow-section: rejection-criteria -->
|
|
112
130
|
## Review Rejection Criteria
|
|
@@ -115,6 +133,10 @@ Reject or revise the patch when any of these appear without strong local justifi
|
|
|
115
133
|
|
|
116
134
|
- New large `clone()` calls, clone-then-borrow code, loop clones, or state clones used only to appease ownership errors.
|
|
117
135
|
- New `Arc<Mutex<AppState>>`-style shared bags, locks held across `.await`, or async I/O resources shared mainly by mutex.
|
|
136
|
+
- New Rust 1.95+ or 1.96+ API usage without MSRV, `rust-version`, edition, toolchain, CI, or fallback evidence.
|
|
137
|
+
- New `LazyLock` initialization for recoverable runtime configuration where permanent panic poisoning would be the wrong failure policy.
|
|
138
|
+
- New `spare_capacity_mut` plus `set_len` without a narrow, proven initialization invariant.
|
|
139
|
+
- New public `impl Trait`, `Deref`, GAT, workspace resolver, feature, or `rust-version` change without public API and compatibility review.
|
|
118
140
|
- New public `'static`, `Send`, or `Sync` bounds that exist only because an internal task was spawned.
|
|
119
141
|
- New public `Box<dyn Error>` in a library where callers need typed failures.
|
|
120
142
|
- New production `unwrap` or vague `expect` on I/O, parse, environment, network, FFI, lock, or user input paths.
|
|
@@ -130,9 +152,11 @@ Reject or revise the patch when any of these appear without strong local justifi
|
|
|
130
152
|
## Postconditions
|
|
131
153
|
|
|
132
154
|
- Ownership changes are intentional, not compiler appeasement.
|
|
155
|
+
- Rust-version-gated syntax, standard-library APIs, Cargo behavior, and lint assumptions match the declared MSRV or have explicit fallbacks.
|
|
133
156
|
- Public API, features, optional dependencies, and error contracts are synchronized.
|
|
134
157
|
- Async runtime ownership is preserved and blocking work is isolated.
|
|
135
|
-
- Unsafe and FFI invariants are preserved or no unsafe code was touched.
|
|
158
|
+
- Unsafe, `unsafe_op_in_unsafe_fn`, and FFI invariants are preserved or no unsafe code was touched.
|
|
159
|
+
- Allocation, initialization, Cargo workspace, and release-profile choices are intentional and reported when they affect public or delivery behavior.
|
|
136
160
|
- Build-loop cost, target/cache impact, smoke-target coverage, and native toolchain prerequisites
|
|
137
161
|
are handled or reported.
|
|
138
162
|
- Missing feature, semver, docs, unsafe, FFI, smoke, package, or performance verification is reported.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
mustflow_doc: skill.service-boundary-architecture
|
|
3
|
+
locale: en
|
|
4
|
+
canonical: true
|
|
5
|
+
revision: 1
|
|
6
|
+
lifecycle: mustflow-owned
|
|
7
|
+
authority: procedure
|
|
8
|
+
name: service-boundary-architecture
|
|
9
|
+
description: Apply this skill when service boundaries, modular-monolith boundaries, data ownership, queue/event boundaries, multi-tenant isolation, failure flow, operational recovery, or large-scale architecture split decisions are designed, reviewed, or changed.
|
|
10
|
+
metadata:
|
|
11
|
+
mustflow_schema: "1"
|
|
12
|
+
mustflow_kind: procedure
|
|
13
|
+
pack_id: mustflow.core
|
|
14
|
+
skill_id: mustflow.core.service-boundary-architecture
|
|
15
|
+
command_intents:
|
|
16
|
+
- changes_status
|
|
17
|
+
- changes_diff_summary
|
|
18
|
+
- test_related
|
|
19
|
+
- test
|
|
20
|
+
- lint
|
|
21
|
+
- build
|
|
22
|
+
- docs_validate_fast
|
|
23
|
+
- test_release
|
|
24
|
+
- mustflow_check
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# Service Boundary Architecture
|
|
28
|
+
|
|
29
|
+
<!-- mustflow-section: purpose -->
|
|
30
|
+
## Purpose
|
|
31
|
+
|
|
32
|
+
Design or review large-system boundaries by starting with ownership, failure, data coupling, and operations. Treat service splits, queues, caches, events, and deployment topology as consequences of the boundary model, not as architecture decorations.
|
|
33
|
+
|
|
34
|
+
This skill protects against the common failure mode where a system is split by nouns, shares data anyway, retries blindly, emits ambiguous events, lacks tenant isolation, and then discovers during an incident that no team owns the truth.
|
|
35
|
+
|
|
36
|
+
<!-- mustflow-section: use-when -->
|
|
37
|
+
## Use When
|
|
38
|
+
|
|
39
|
+
- A task designs, reviews, documents, or changes service boundaries, modular-monolith boundaries, bounded contexts, team ownership, API ownership, event ownership, queue ownership, or data ownership.
|
|
40
|
+
- A monolith is being prepared for future split, a microservice split is proposed, or a service/module boundary is causing deploy, data, incident, or team coordination pain.
|
|
41
|
+
- The task touches cross-service transactions, outbox/inbox patterns, idempotency, retries, timeouts, caches, read models, search models, shared databases, operational tools, manual correction, observability, tenancy, hot keys, or failure recovery.
|
|
42
|
+
- Architecture docs, decision records, onboarding docs, or skills need durable guidance for large-system design and operational readiness.
|
|
43
|
+
|
|
44
|
+
<!-- mustflow-section: do-not-use-when -->
|
|
45
|
+
## Do Not Use When
|
|
46
|
+
|
|
47
|
+
- The task is a small local refactor with no ownership, data, deployment, or operational boundary decision; use `architecture-deepening-review` or `behavior-preserving-refactor`.
|
|
48
|
+
- The task only changes a single API shape; use `api-contract-change`.
|
|
49
|
+
- The task only changes database schema or persistence safety inside one owner; use `database-change-safety` or `database-migration-change`.
|
|
50
|
+
- The task only changes one external provider adapter; use `adapter-boundary`.
|
|
51
|
+
- The task only models one lifecycle state machine; use `state-machine-pattern`.
|
|
52
|
+
|
|
53
|
+
<!-- mustflow-section: required-inputs -->
|
|
54
|
+
## Required Inputs
|
|
55
|
+
|
|
56
|
+
- Candidate domains, capabilities, teams, modules, services, and the reason each boundary may need to exist.
|
|
57
|
+
- Data ownership map: which module or service is the source of truth for each core fact, identifier, status, ledger, permission, tenant record, event, and read projection.
|
|
58
|
+
- Current or proposed communication paths: direct calls, APIs, events, queues, shared databases, shared caches, batch jobs, search indexes, analytics stores, files, and admin tools.
|
|
59
|
+
- Failure flows: duplicate requests, partial success, timeout, retry, consumer crash, queue backlog, dead-letter handling, external provider uncertainty, reconciliation, and manual correction.
|
|
60
|
+
- Consistency expectations: strong consistency, eventual consistency, tolerated delay, stale reads, cache invalidation, read/write split, search delay, and deletion or retention rules.
|
|
61
|
+
- Tenant, authorization, observability, deployment, migration, and operations requirements.
|
|
62
|
+
- Configured verification intents and any existing architecture decision records or context files.
|
|
63
|
+
|
|
64
|
+
<!-- mustflow-section: preconditions -->
|
|
65
|
+
## Preconditions
|
|
66
|
+
|
|
67
|
+
- Do not start by choosing microservices, Kafka, Kubernetes, service mesh, CQRS, or event sourcing. Start by identifying ownership and failure pressure.
|
|
68
|
+
- Do not split by nouns alone. Check which rules and data change together, which teams own them, and which failures must be isolated.
|
|
69
|
+
- Treat shared databases, shared caches, and table reads across owners as coupling evidence even when code appears separated.
|
|
70
|
+
- Treat architecture diagrams as secondary evidence. Decision records, ownership maps, failure behavior, and recovery tools are the durable contract.
|
|
71
|
+
|
|
72
|
+
<!-- mustflow-section: allowed-edits -->
|
|
73
|
+
## Allowed Edits
|
|
74
|
+
|
|
75
|
+
- Architecture docs, decision records, context files, workflow skills, tests, fixtures, schema notes, operational runbook notes, and directly related source changes.
|
|
76
|
+
- Boundary diagrams only when they are backed by ownership, data, failure, and operations text.
|
|
77
|
+
- Module, service, API, event, queue, cache, read-model, or operations-tool boundaries when the repository scope and command contract support that edit.
|
|
78
|
+
- Do not introduce new infrastructure, service topology, queue systems, brokers, databases, orchestration platforms, or deployment tools unless the user explicitly requests that implementation and the repository already has an approved path.
|
|
79
|
+
|
|
80
|
+
<!-- mustflow-section: procedure -->
|
|
81
|
+
## Procedure
|
|
82
|
+
|
|
83
|
+
1. Name the business capabilities and the facts each capability owns. For every core fact, identify exactly one source of truth and the owner responsible for correcting it.
|
|
84
|
+
2. Split first by reason to change, not by noun. Keep code together when it changes together; split only when ownership, release cadence, scale, compliance, failure isolation, or team autonomy makes the boundary useful.
|
|
85
|
+
3. Prefer a modular monolith with hard internal boundaries when the team is still discovering the domain. Draw future split lines early, but do not pay distributed-system costs before the boundary pressure is real.
|
|
86
|
+
4. Reject shared database ownership. If another service or module needs data, choose an API, event, read projection, export, or explicitly owned query surface instead of direct table coupling.
|
|
87
|
+
5. Design failure flows before the happy path. Cover partial success, duplicated requests, timeout, unknown external outcome, consumer crash, queue backlog, dead-letter handling, replay, reconciliation, and manual recovery.
|
|
88
|
+
6. Require idempotency for commands that create, charge, reserve, approve, issue, grant, cancel, refund, redeem, or mutate durable state. Use stable request keys and return the previous result for duplicate keys.
|
|
89
|
+
7. Treat queues as storage and backpressure, not magic. Define retry policy, exponential backoff or jitter, max attempts, retention, dead-letter ownership, consumer scaling, ordering expectations, replay safety, and loss tolerance.
|
|
90
|
+
8. Every network call across the boundary needs a timeout, bounded retry policy, fallback or failure result, duplicate-safety story, and observability identifiers.
|
|
91
|
+
9. Treat caches as a consistency tradeoff. Declare which stale values are acceptable, max staleness, invalidation owner, tenant and permission visibility, and whether the cache is allowed to become authority.
|
|
92
|
+
10. Model authorization, tenant isolation, audit, and data visibility at the boundary. Tenant ID must travel through APIs, queues, caches, logs, files, reports, and admin tools when the system is multi-tenant.
|
|
93
|
+
11. For event-driven boundaries, use past-tense fact events such as `OrderCreated` or `PaymentApproved`. Do not disguise commands as events. Include event ID, version, occurred time, publisher, subject, causation or request ID, correlation or trace ID, and compatible schema evolution rules.
|
|
94
|
+
12. Separate transaction data from search, reporting, analytics, and screen-shaped read projections when their consistency, indexing, query, or retention needs diverge.
|
|
95
|
+
13. Define tolerated delay with numbers. Replace vague "real-time" claims with accepted latency or freshness windows such as seconds, minutes, or batch cadence.
|
|
96
|
+
14. Check hot keys and noisy neighbors. Define per-tenant, per-customer, per-room, per-campaign, or per-product limits, queue separation, worker pool separation, connection caps, and external API call caps when one key can dominate load.
|
|
97
|
+
15. Keep domain rules in the owning domain layer. Do not scatter refund windows, cancellation rules, entitlement checks, inventory reservations, or admin overrides across controllers, UI, workers, and jobs.
|
|
98
|
+
16. Plan migrations with expand, dual-write or compatibility, backfill, read switch, verification, and contract removal. Do not rename or remove shared fields in one deployment step.
|
|
99
|
+
17. Plan deletion and retention before data is created. Identify legal retention, anonymization, log masking, backup retention, search/read-model cleanup, and audit exceptions.
|
|
100
|
+
18. Make observability a product feature. Connect API, queue, worker, external provider, database, and admin operations with trace, request, correlation, causation, job, and tenant identifiers. Prefer p95, p99, error rate, queue age, backlog, dependency failure rate, and business success rate over averages.
|
|
101
|
+
19. Make deployment and rollback a boundary decision. Use feature flags, canaries, partial exposure, compatibility windows, and kill switches for risky boundary changes.
|
|
102
|
+
20. Provide operational tools for reality: reconcile external state, retry or quarantine failed messages, correct user/account/order state, revoke permissions, inspect provider responses, and undo manual changes with audit and approval when required.
|
|
103
|
+
21. Record the decision: selected boundary, rejected alternatives, reason, assumptions, failure modes, data owner, operational owner, verification, and revisit triggers.
|
|
104
|
+
|
|
105
|
+
<!-- mustflow-section: review-rejection-criteria -->
|
|
106
|
+
## Review Rejection Criteria
|
|
107
|
+
|
|
108
|
+
Reject or revise the design when:
|
|
109
|
+
|
|
110
|
+
- A service or module boundary has no named data owner.
|
|
111
|
+
- Two owners write the same core fact without a conflict rule.
|
|
112
|
+
- A service reads another service's tables or cache as normal behavior.
|
|
113
|
+
- The design shows only the happy path.
|
|
114
|
+
- Retries lack timeout, backoff, jitter, max attempts, idempotency, or failure ownership.
|
|
115
|
+
- A queue has no dead-letter, retention, replay, ordering, or backlog policy.
|
|
116
|
+
- Cache behavior does not state acceptable staleness and invalidation ownership.
|
|
117
|
+
- Events are imperative commands instead of past-tense facts.
|
|
118
|
+
- Event schemas lack version, event ID, occurred time, publisher, and correlation or causation identifiers.
|
|
119
|
+
- Tenant, permission, audit, or data-retention boundaries are added after the fact.
|
|
120
|
+
- Architecture choice is justified by tooling fashion instead of ownership, failure, scale, compliance, or team autonomy.
|
|
121
|
+
- Manual operations can mutate critical state without audit, approval, and reversal or reconciliation path.
|
|
122
|
+
|
|
123
|
+
<!-- mustflow-section: postconditions -->
|
|
124
|
+
## Postconditions
|
|
125
|
+
|
|
126
|
+
- Data ownership and source-of-truth boundaries are explicit.
|
|
127
|
+
- Boundary split is justified by change reason, ownership, scale, compliance, failure isolation, or team autonomy.
|
|
128
|
+
- Failure handling, idempotency, retry, queue, cache, event, and observability contracts are defined or explicitly deferred.
|
|
129
|
+
- Tenant, authorization, deletion, retention, migration, deployment, and operational recovery risks are checked or reported.
|
|
130
|
+
- Decision records explain why this boundary was chosen, what alternatives were rejected, and when to revisit.
|
|
131
|
+
|
|
132
|
+
<!-- mustflow-section: verification -->
|
|
133
|
+
## Verification
|
|
134
|
+
|
|
135
|
+
Use configured oneshot command intents when available:
|
|
136
|
+
|
|
137
|
+
- `changes_status`
|
|
138
|
+
- `changes_diff_summary`
|
|
139
|
+
- `test_related`
|
|
140
|
+
- `test`
|
|
141
|
+
- `lint`
|
|
142
|
+
- `build`
|
|
143
|
+
- `docs_validate_fast`
|
|
144
|
+
- `test_release`
|
|
145
|
+
- `mustflow_check`
|
|
146
|
+
|
|
147
|
+
Use broader configured checks when the boundary change affects templates, public docs, package behavior, or cross-module contracts. Report missing architecture, integration, load, or operational verification instead of claiming the design is production-ready.
|
|
148
|
+
|
|
149
|
+
<!-- mustflow-section: failure-handling -->
|
|
150
|
+
## Failure Handling
|
|
151
|
+
|
|
152
|
+
- If ownership cannot be assigned, stop at a decision record or open question instead of drawing a false boundary.
|
|
153
|
+
- If shared database coupling is unavoidable for now, document it as temporary coupling with the exit path, owner, read/write limits, and revisit trigger.
|
|
154
|
+
- If the user asks for a new service but failure, data ownership, or operational tooling is missing, recommend the smallest boundary-preserving modular step first.
|
|
155
|
+
- If verification is unavailable, report which boundary risks remain untested: duplicate command, stale read, queue replay, provider timeout, tenant isolation, migration, deletion, or manual recovery.
|
|
156
|
+
|
|
157
|
+
<!-- mustflow-section: output-format -->
|
|
158
|
+
## Output Format
|
|
159
|
+
|
|
160
|
+
- Boundary checked
|
|
161
|
+
- Data owners and source-of-truth notes
|
|
162
|
+
- Failure, idempotency, queue, cache, and event notes
|
|
163
|
+
- Tenant, auth, retention, observability, deployment, and operations notes
|
|
164
|
+
- Files changed
|
|
165
|
+
- Command intents run
|
|
166
|
+
- Skipped checks and reasons
|
|
167
|
+
- Remaining service-boundary risk
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
mustflow_doc: skill.tailwind-code-change
|
|
3
3
|
locale: en
|
|
4
4
|
canonical: true
|
|
5
|
-
revision:
|
|
5
|
+
revision: 3
|
|
6
6
|
lifecycle: mustflow-owned
|
|
7
7
|
authority: procedure
|
|
8
8
|
name: tailwind-code-change
|
|
9
|
-
description: Apply this skill when Tailwind classes, className composition, static class detection, theme tokens, variants, safelists, arbitrary values, Tailwind config, @theme, @apply, or Tailwind migration surfaces are created or changed.
|
|
9
|
+
description: Apply this skill when Tailwind classes, className composition, static class detection, theme tokens, variants, safelists, arbitrary values, Tailwind config, v4 CSS-first configuration, @source, @theme, @reference, @apply, or Tailwind migration surfaces are created or changed.
|
|
10
10
|
metadata:
|
|
11
11
|
mustflow_schema: "1"
|
|
12
12
|
mustflow_kind: procedure
|
|
@@ -33,8 +33,8 @@ Preserve Tailwind static class detection, design tokens, bounded variants, respo
|
|
|
33
33
|
<!-- mustflow-section: use-when -->
|
|
34
34
|
## Use When
|
|
35
35
|
|
|
36
|
-
- Tailwind `class`, `className`, `@apply`, `@theme`, config, source scanning, safelist, theme tokens, variants, arbitrary values, or component class composition change.
|
|
37
|
-
- The task touches Tailwind v3/v4 migration, token addition, responsive modifiers, state modifiers, `clsx`/`cn`, CVA-style variant helpers, `@source`, or production CSS generation risk.
|
|
36
|
+
- Tailwind `class`, `className`, `@apply`, `@theme`, `@source`, `@reference`, config, source scanning, safelist, theme tokens, variants, arbitrary values, or component class composition change.
|
|
37
|
+
- The task touches Tailwind v3/v4 migration, CSS-first configuration, token addition, responsive modifiers, state modifiers, `clsx`/`cn`, CVA-style variant helpers, `source(none)`, `@source inline`, `@source not inline`, or production CSS generation risk.
|
|
38
38
|
- Component APIs accept status, tone, intent, size, column count, color, spacing, or class-related props that affect Tailwind utilities.
|
|
39
39
|
|
|
40
40
|
<!-- mustflow-section: do-not-use-when -->
|
|
@@ -46,16 +46,18 @@ Preserve Tailwind static class detection, design tokens, bounded variants, respo
|
|
|
46
46
|
<!-- mustflow-section: required-inputs -->
|
|
47
47
|
## Required Inputs
|
|
48
48
|
|
|
49
|
-
- Tailwind version/config or CSS entry, content/source scanning rules, theme tokens, PostCSS/build config, class merge helpers, variant helpers, component patterns, and tests.
|
|
49
|
+
- Tailwind version/config or CSS entry, `@import` options, `@source` directives, content/source scanning rules, theme tokens, PostCSS/build config, class merge helpers, variant helpers, component patterns, and tests.
|
|
50
50
|
- Existing design system vocabulary, token naming conventions, accessibility state patterns, and component prop APIs.
|
|
51
51
|
- Any runtime source for visual values: props, CMS, database, tenant config, user input, API data, or external package markup.
|
|
52
|
+
- Framework style boundaries that may need `@reference`, such as Vue or Svelte component styles, CSS Modules, scoped CSS, or separate CSS entrypoints.
|
|
52
53
|
- Configured verification intents.
|
|
53
54
|
|
|
54
55
|
<!-- mustflow-section: preconditions -->
|
|
55
56
|
## Preconditions
|
|
56
57
|
|
|
57
|
-
- Confirm how Tailwind detects source classes before changing class composition.
|
|
58
|
+
- Confirm how Tailwind detects source classes before changing class composition, helper-file location, or CSS entry imports.
|
|
58
59
|
- Identify whether the project is using Tailwind v3 config safelists or Tailwind v4 CSS source directives.
|
|
60
|
+
- Identify whether Tailwind v4 source detection is automatic, disabled with `source(none)`, extended with `@source`, or narrowed with `@source not inline`.
|
|
59
61
|
- Identify tokens, variant helpers, and semantic visual states before adding arbitrary values or raw colors.
|
|
60
62
|
- Confirm whether affected components are local one-offs, shared components, or public design-system surfaces.
|
|
61
63
|
|
|
@@ -65,7 +67,9 @@ Preserve Tailwind static class detection, design tokens, bounded variants, respo
|
|
|
65
67
|
- Use full static class strings that the build can detect.
|
|
66
68
|
- Use existing theme tokens before arbitrary values or raw colors.
|
|
67
69
|
- Use static maps, component extraction, or variant helpers for finite repeated variants.
|
|
68
|
-
- Use safelists only for finite class candidates that cannot appear in scanned source.
|
|
70
|
+
- Use safelists or `@source inline` only for finite class candidates that cannot appear in scanned source.
|
|
71
|
+
- Use `@source not inline` or equivalent source policy to block known false-positive candidates when generated CSS would otherwise include unwanted utilities.
|
|
72
|
+
- Use `@reference` when component-scoped styles need access to Tailwind theme variables, custom utilities, or variants without duplicating emitted CSS.
|
|
69
73
|
- Use inline styles plus CSS variables for unbounded runtime values such as tenant, database, or user-provided colors.
|
|
70
74
|
- Keep hover, focus-visible, disabled, aria/data, dark, and motion variants aligned with existing component behavior.
|
|
71
75
|
|
|
@@ -74,29 +78,35 @@ Preserve Tailwind static class detection, design tokens, bounded variants, respo
|
|
|
74
78
|
|
|
75
79
|
1. Read Tailwind config or CSS entry, scanning rules, theme tokens, helpers, and nearby components.
|
|
76
80
|
2. Classify the change: token, utility usage, dynamic variant, component extraction, responsive state, accessibility state, or migration.
|
|
77
|
-
3.
|
|
78
|
-
4.
|
|
79
|
-
5.
|
|
80
|
-
6. Use
|
|
81
|
-
7. Use
|
|
82
|
-
8.
|
|
83
|
-
9.
|
|
84
|
-
10.
|
|
85
|
-
11.
|
|
86
|
-
12.
|
|
87
|
-
13.
|
|
88
|
-
14. Do not
|
|
89
|
-
15.
|
|
90
|
-
16.
|
|
91
|
-
17.
|
|
81
|
+
3. For Tailwind v4, classify CSS-first configuration before touching utility usage: `@theme`, `@utility`, `@variant`, `@custom-variant`, `@source`, `@source inline`, `@source not inline`, `source(none)`, and `@reference`. Do not apply v3 config instincts to a v4 CSS entry without checking the project pattern.
|
|
82
|
+
4. Treat every Tailwind class as a complete build-time token. Reject runtime interpolation, concatenation, array joins, or fragment assembly for utilities such as color, spacing, grid count, span, tone, or arbitrary values.
|
|
83
|
+
5. For finite choices, use a static map with semantic keys and full class strings. Good keys describe UI meaning such as `danger`, `muted`, `compact`, or `featured`; bad keys store Tailwind fragments such as `red`, `600`, or `cols-3` for later assembly.
|
|
84
|
+
6. Use a CVA-style variant helper only when a shared component has multiple variant axes, defaults, or compound variants. The helper must still contain full static class strings.
|
|
85
|
+
7. Use safelists only as a last resort for finite candidates that cannot be present in source files. Tailwind v4 projects should express this through the project's CSS source policy; Tailwind v3 maintenance projects may use config safelists when that is the existing pattern. Do not safelist broad palettes or unbounded ranges to avoid fixing a bad component API.
|
|
86
|
+
8. Keep `@source inline` bounded, named, and close to the CSS entry policy. If `source(none)` disables automatic detection, require explicit source includes for every package, app, and external component source that owns utilities. Use `@source not inline` for known false positives instead of relying on accidental absence.
|
|
87
|
+
9. Use inline style plus a CSS variable bridge for unbounded runtime values from databases, APIs, tenants, CMS content, or user input. Do not try to mint Tailwind class names from unbounded values.
|
|
88
|
+
10. Treat arbitrary values as escape hatches. Allow them for one-off asset alignment, complex grid templates, complex calculations, local CSS variable assignment, or values that cannot be expressed with regular utilities.
|
|
89
|
+
11. Reject arbitrary raw colors in component markup, arbitrary spacing that approximates existing scale values, arbitrary container widths, arbitrary radius or shadow used as a hidden token, and repeated arbitrary values. Repeated values must be promoted to a semantic design token or component variant.
|
|
90
|
+
12. Keep raw color literals in theme or token files only. Token names should describe purpose, not numeric value.
|
|
91
|
+
13. Use `clsx` or `cn` only to combine complete class strings. Do not hide dynamic Tailwind string construction inside helper functions.
|
|
92
|
+
14. Do not expose unrestricted class fragments through public component APIs. If a `className` passthrough exists, preserve internal layout-critical classes and prevent consumer props from becoming the source of required Tailwind generation.
|
|
93
|
+
15. Do not extract a component only because a class list is long. Extract a component when structure, behavior, accessibility, and styling repeat together across files.
|
|
94
|
+
16. Do not use `@apply` to hide long JSX or template class lists. Reserve `@apply` for third-party markup overrides, CSS-module boundaries, or template systems where component extraction is genuinely heavier.
|
|
95
|
+
17. When `@apply` or `@variant` appears in component-scoped CSS, verify that `@reference` points at the shared Tailwind CSS entry or theme source without emitting duplicate CSS.
|
|
96
|
+
18. Use mobile-first responsive classes and include focus-visible or equivalent keyboard states for interactive elements.
|
|
97
|
+
19. Prefer container, data, aria, `has`, `group`, `peer`, `not`, `supports`, `starting`, motion, and dark-mode variants only when the source selector/state is owned and testable. Keep selector-based dark variants and `color-scheme` aligned with the root theme strategy.
|
|
98
|
+
20. Check layout utilities for common browser traps: missing `min-w-0` or `min-h-0` in flex/grid children, viewport height assumptions that should use `dvh`, `svh`, or `lvh`, and `space-*` on wrapped, reordered, or grid-like children where `gap` is the stable contract.
|
|
99
|
+
21. Check for conflicting utilities on one element, such as mutually exclusive display, spacing, text-size, viewport, or layout utilities.
|
|
100
|
+
22. Choose configured verification intents that cover production build, lint, component tests, accessibility states, and visual risk when available.
|
|
92
101
|
|
|
93
102
|
<!-- mustflow-section: generation-policy -->
|
|
94
103
|
## Generation Policy
|
|
95
104
|
|
|
96
|
-
- Tailwind classes must appear as complete static strings in scanned source, a static variant map, a CVA-style variant declaration,
|
|
105
|
+
- Tailwind classes must appear as complete static strings in scanned source, a static variant map, a CVA-style variant declaration, an explicit finite safelist, or a bounded v4 CSS source directive.
|
|
97
106
|
- Dynamic visual state must be represented by exactly one of: static map entry, variant helper entry, explicit safelist entry, or CSS variable bridge.
|
|
98
107
|
- Status, tone, intent, size, density, and column props are closed sets until proven otherwise. Model them as finite typed variants, not string fragments.
|
|
99
108
|
- Production CSS is the contract. If a class only exists after runtime string construction, assume production CSS may omit it.
|
|
109
|
+
- `@source inline`, `@source not inline`, and `source(none)` are part of the production CSS generation contract. Review them as carefully as code that uses utilities.
|
|
100
110
|
|
|
101
111
|
<!-- mustflow-section: token-policy -->
|
|
102
112
|
## Token Policy
|
|
@@ -117,8 +127,12 @@ Reject the change when:
|
|
|
117
127
|
- The same arbitrary value appears in more than one place.
|
|
118
128
|
- A wrapper component exists only to hide a one-off class list.
|
|
119
129
|
- `@apply` hides component-specific styling instead of handling a CSS boundary.
|
|
130
|
+
- Component-scoped `@apply` or `@variant` relies on Tailwind globals without the needed `@reference` boundary.
|
|
120
131
|
- A public component exposes unconstrained Tailwind class fragments.
|
|
121
132
|
- A safelist covers broad palettes, broad numeric ranges, or unknown runtime values.
|
|
133
|
+
- `@source inline` covers broad palettes, broad numeric ranges, or unknown runtime values.
|
|
134
|
+
- `source(none)` is used without explicit source includes for all utility-owning files.
|
|
135
|
+
- `space-*` is used where wrapped or reordered children require `gap`.
|
|
122
136
|
- Conflicting utilities are present on the same element.
|
|
123
137
|
|
|
124
138
|
<!-- mustflow-section: postconditions -->
|