| name | lwc-3-decisions |
| description | Soft-prompt the LWC author with three load-bearing architectural questions before they create or substantially edit a Lightning Web Component — Communication, State, Responsibility. Always elaborates *why* each question matters with concrete what-goes-wrong examples, then asks. Skipping is allowed but logged in the response so the choice is conscious. TRIGGER when creating or substantially editing files under `force-app/**/lwc/**`, `examples/**/lwc/**`, or any `*.js` / `*.html` / `*.js-meta.xml` inside an `lwc/` folder. Also TRIGGER when the user mentions "new component", "split this component", "refactor this LWC", "props vs events", "wire vs imperative", "LMS", "pubsub", or "where should this live". Skip on tiny edits (typo fixes, single-line CSS, comment-only changes) — invoke only for changes that touch component contract, data flow, or boundaries. |
The 3 Decisions Skill — Communication, State, Responsibility
This is the load-bearing skill of the toolkit. It encodes the rule that LWC apps almost never fail from missing features in the framework — they fail because nobody made these three decisions consciously, so the team made them by accident a hundred times in slightly different ways.
Your job when this skill activates: walk the author through the three decisions in soft-prompt mode before letting LWC code be written or substantially changed. Always elaborate first, then ask. Always allow skip, and always note the skip in the final response so the team has an audit trail of the call.
Activation rules — what counts as "substantial"
Trigger the full walk-through when the change involves any of:
- Creating a new LWC component (any new folder under
lwc/)
- Adding a
@api property or a custom event to an existing component (changes the component contract)
- Adding or changing
@wire, getRecord, getRecords, lightning/uiRecordApi, imperative Apex calls (changes data flow)
- Adding or removing parent–child placement, slot usage, or LMS publish/subscribe (changes communication)
- Splitting one component into many or merging many back into one (changes responsibility)
Skip the walk-through (just edit) when the change is:
- A typo, comment, formatting, or pure CSS visual tweak
- Internal refactor that doesn't change inputs, outputs, or rendered DOM contract
- A test-only change
When uncertain, ask one clarifying line ("Is this a contract change or just internal cleanup?") and decide from the answer.
How to deliver the walk-through
-
One line of context first. Tell the author what you're about to do and why, in one sentence. Example: "Before I touch this component, three quick decisions — they're the ones that quietly sink LWC codebases. Each comes with a why and a skip option."
-
Ask the three decisions one at a time. Not all at once — that makes it feel like a form. One at a time, each with its elaboration block. After each, wait for the answer or for "skip".
-
Always elaborate before asking. The user explicitly asked for elaboration. Two to four short sentences per question. Use the elaboration blocks below — adapt the wording to the specific component the author is touching, but keep the substance.
-
Accept "skip", "obvious", "you decide", "later", etc. as a soft-skip. Don't argue. Move to the next decision. At the end, summarize what was answered and what was skipped.
-
Log the answers in the response Claude gives back. Even if everything was skipped, end with a one-paragraph "Decisions taken: …" so the author has a record. If a decision was skipped, write "skipped — [Claude's best inference of what's likely happening]" so the inferred default is visible.
-
Then proceed with the actual code change. This skill does not block. It informs.
Decision 1 — Communication
One-line ask: "How will this component talk to the others?"
Elaboration to deliver verbatim or adapted (the why):
This is the cheapest decision to make and the most expensive to defer. Communication patterns in LWC come in three shapes: parent-to-child via @api props going down and CustomEvent going up; sibling-to-sibling with no direct relation via the Lightning Message Service; and "everything else", which is almost always a sign that the architecture is wrong, not the communication.
When teams skip this question, they reach for a global event bus or a window-level pubsub the moment two unrelated components need to talk. That works on day one. By month six, the wiring is invisible, every refactor breaks something three components away, and nobody knows who fires which event.
Acceptable answers: "Parent–child, props down, events up." / "No relation, global signal, LMS." / "No good answer here yet — let me rethink the placement." The last one is also acceptable; it's the unconscious answer that's dangerous, not the uncertain one.
Then ask:
Which of these does this component fall into — parent–child, LMS, or stop-and-rethink?
What to flag in the author's answer:
- "Pubsub" / "event bus" / "window.dispatchEvent" without LMS → push back. LMS exists for this exact reason.
- "Parent will set @api property after rendering" → fine, but ask about the loading state contract.
- "Child will emit and parent will re-fetch from Apex" → fine, but flag in Decision 2 — that's a state ownership choice.
- "Doesn't really need to talk to anything" → great. Move on.
Decision 2 — State
One-line ask: "Where does the source of truth for this component's data live?"
Elaboration to deliver verbatim or adapted (the why):
Data ownership is the decision that AI assistants will make for you the loudest. If you don't pick a strategy, the assistant will reach for @wire(getRecord) in one component and imperative Apex + manual cache in the next, and now your UI shows two values for the same record because neither component knows the other exists.
The question is not @wire vs imperative. That's a tactic. The strategy is who owns this slice of data. Acceptable shapes: a parent component owns it and passes down via prop; a wire adapter owns it and the cache is the truth; a dedicated service module owns it and components subscribe; the user-record getRecord adapter owns it (Salesforce's own cache).
When this decision is skipped, the symptom is "the UI is inconsistent and nobody knows why." That bug is almost always a missing state strategy, not a missing refresh call.
Then ask:
Who owns this data — this component, a parent, a wire adapter (which one), a shared service, or getRecord's cache?
What to flag in the author's answer:
- "Each component fetches it separately" → flag. That's the duplicate-data antipattern. Suggest one of the acceptable shapes.
- "I'll cache it in a module-level variable" → acceptable but explicitly a service-module pattern. Flag the lifecycle (when does it invalidate? what triggers a re-fetch?).
- "@wire with the same parameters in two components" → fine; LDS deduplicates. Confirm both components actually read the same shape.
- Silence / "I'll figure it out" → soft-skip. Note in the response what you're going to assume — usually
@wire(getRecord) for record data, imperative for everything else — so the inference is visible.
Decision 3 — Responsibility
One-line ask: "Where does this component's job end and another's begin?"
Elaboration to deliver verbatim or adapted (the why):
Responsibility is the decision that gets dressed up as "should this be one component or three?" — but component count is not the question. The question is boundaries: what is this component answerable for, and what is it explicitly not answerable for. A 200-line component with a clear contract is fine. Three 50-line components that each peek into each other's slots and DOM are a disaster.
When responsibility is unclear, two predictable failure modes appear: monolith (one component does everything, nobody wants to touch it) and chaos (everything was split, but the wiring is so tangled it might as well be one component, except now you can't even read it top to bottom). Both come from the same root cause — boundaries were never decided, only inherited.
Good answer shape: a one-sentence contract. "This component is responsible for displaying X based on inputs Y, and emits event Z when the user does W. It does not fetch data, it does not navigate, it does not mutate." If you can write that sentence, you have a boundary.
Then ask:
What is this component answerable for, and what is it deliberately not answerable for?
What to flag in the author's answer:
- "It does X, Y, Z, and also fetches data, and also handles navigation" → soft push back. Three things in one component is fine; eight is not. Suggest the most natural cut.
- "It just renders what the parent gives it" → ideal. Atomic component. Note in response.
- "Not sure yet, depends on what the backend returns" → soft-skip with a flag. Note that the boundary should be drawn from the UI side, not the backend shape.
- "It's like the parent component but slightly different" → flag. This is usually a sign that the parent's responsibility is wrong, not that you need a sibling.
After all three are answered
End the message with a short structured summary that lives in the author's chat history:
## Decisions taken
- Communication: <answer or "skipped — assumed parent–child, props down, events up">
- State: <answer or "skipped — assumed @wire(getRecord)">
- Responsibility: <answer or "skipped — boundary inferred from current code">
## Notes
- <any flags raised during the walk-through>
- <any follow-up suggested>
This summary is the audit trail. It survives the conversation.
Red flags that escalate to lwc-architect
If during the walk-through any of the following appears, suggest the author run the lwc-architect agent for a deeper design pass before writing code:
- Three or more components are being introduced at once
- The component is meant to live in both Locker and LWR contexts (run
/locker-check afterward as well)
- The component is going to expose
@api methods, not just properties
- The component will use
lwc:dom="manual", slots inside slots, or render-blocking wires
- Two of the three answers were "skip"
What this skill is NOT
- Not a code review skill — that's
/quality-warden.
- Not a security skill — that's
appexchange-review-patterns.
- Not a runtime compatibility skill — that's
locker-lwr-compat.
- Not a blocker — soft-prompt only. The author can always say "skip" or "just write it".
The single purpose is: make the three decisions visible, conscious, and logged. That alone changes how an LWC codebase ages.