@rmrdeveloper/sideroom-pi 5.0.0 → 6.0.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/README.md +108 -79
- package/{src/assets → assets}/artifacts/GUIDELINES_TEMPLATE.md +40 -11
- package/extensions/ask/execute.ts +56 -0
- package/extensions/ask/index.ts +77 -0
- package/extensions/ask/model.ts +196 -0
- package/extensions/ask/ui.ts +445 -0
- package/extensions/modified-files/index.ts +45 -0
- package/extensions/modified-files/model.ts +74 -0
- package/extensions/modified-files/session.ts +200 -0
- package/extensions/modified-files/ui.ts +207 -0
- package/extensions/todo/execute.ts +69 -0
- package/extensions/todo/guards.ts +107 -0
- package/extensions/todo/index.ts +91 -0
- package/extensions/todo/model.ts +327 -0
- package/extensions/todo/session.ts +166 -0
- package/extensions/todo/ui.ts +41 -0
- package/package.json +26 -27
- package/dist/pi-extension.js +0 -1175
- package/skills/sideroom-critic/SKILL.md +0 -32
- package/skills/sideroom-grilling/SKILL.md +0 -81
- package/skills/sideroom-spec/SKILL.md +0 -38
- package/skills/sideroom-transcribe-audio/SKILL.md +0 -38
- package/skills/sideroom-transcribe-audio/scripts/transcribe.py +0 -40
- package/src/assets/agents/sideroom-code-reviewer.md +0 -30
- package/src/assets/agents/sideroom-fixer.md +0 -37
- package/src/assets/agents/sideroom-implementer.md +0 -39
- package/src/assets/agents/sideroom-planner.md +0 -31
- package/src/assets/agents/sideroom-verifier.md +0 -32
- package/src/assets/artifacts/guidelines/java.md +0 -211
- package/src/assets/artifacts/guidelines/javascript.md +0 -235
- package/src/assets/artifacts/guidelines/php-laravel.md +0 -210
- package/src/assets/artifacts/guidelines/python.md +0 -227
- package/src/assets/artifacts/guidelines/typescript.md +0 -231
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-critic
|
|
3
|
-
description: Adversarially stress-test a plan or diff for concrete logical, architectural, and scope failures.
|
|
4
|
-
license: MIT
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Adversarial critic
|
|
8
|
-
|
|
9
|
-
Use this skill only when the user explicitly requests a red-team review of a
|
|
10
|
-
non-trivial plan or implementation. It is not an automatic replacement for the
|
|
11
|
-
pipeline reviewer or verifier.
|
|
12
|
-
|
|
13
|
-
Read repository instructions, relevant domain context, the plan or diff, and
|
|
14
|
-
the affected code before drawing conclusions. Verify facts with the available
|
|
15
|
-
tools; do not report speculative risks.
|
|
16
|
-
|
|
17
|
-
Attack only concrete concerns that ordinary implementation review can miss:
|
|
18
|
-
|
|
19
|
-
- a reproducible edge case, invalid state, race, or broken invariant;
|
|
20
|
-
- an unsupported business assumption, with the missing evidence identified;
|
|
21
|
-
- a real dependency-direction, Law of Demeter, CQS, composition, or
|
|
22
|
-
validate-once violation, including the concrete call or import chain;
|
|
23
|
-
- scope mismatch, YAGNI, or a required case that is unhandled.
|
|
24
|
-
|
|
25
|
-
For each finding, give a counterexample or precise consequence. Do not report
|
|
26
|
-
formatting, naming, or generic style preferences. Do not edit code.
|
|
27
|
-
|
|
28
|
-
Return either `No findings` or one record per issue:
|
|
29
|
-
|
|
30
|
-
```
|
|
31
|
-
FINDING <N> | <Critical|High|Medium|Low> | <file:line> | <rule> | <description>
|
|
32
|
-
```
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-grilling
|
|
3
|
-
description: Clarify a non-trivial feature, plan, or decision through dependency-aware question rounds before implementation.
|
|
4
|
-
license: MIT
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Grilling
|
|
8
|
-
|
|
9
|
-
Reach a shared understanding before a non-trivial change is planned or built.
|
|
10
|
-
Model the work as a **design tree**: each decision can unlock further decisions.
|
|
11
|
-
Do not silently choose a product, scope, or trade-off decision for the user.
|
|
12
|
-
|
|
13
|
-
## Read facts; ask for decisions
|
|
14
|
-
|
|
15
|
-
Read existing repository instructions, source, tests, package scripts, and any
|
|
16
|
-
`CONTEXT.md` or `CONTEXT-MAP.md` before the first round. Facts such as current
|
|
17
|
-
behavior, available commands, and domain vocabulary are yours to verify. Never
|
|
18
|
-
ask the user for a fact you can inspect.
|
|
19
|
-
|
|
20
|
-
When a context file exists, use its vocabulary exactly. A context term is a
|
|
21
|
-
domain concept, not a generic technical word. Prefer one canonical term; use
|
|
22
|
-
`Avoid:` terms to prevent synonyms from blurring a decision. If the repository
|
|
23
|
-
has multiple contexts, use its map to identify the relevant one and ask only
|
|
24
|
-
when the relationship remains unclear. Do not create context files or any
|
|
25
|
-
project-local Sideroom state.
|
|
26
|
-
|
|
27
|
-
The user owns decisions: desired outcome, scope, compatibility, user-visible
|
|
28
|
-
behavior, priorities, and accepted trade-offs.
|
|
29
|
-
|
|
30
|
-
## Rounds and frontier
|
|
31
|
-
|
|
32
|
-
The **frontier** contains decisions whose prerequisites are already settled.
|
|
33
|
-
Ask no more than three independent frontier questions in one round. A question
|
|
34
|
-
that depends on another unanswered question belongs to a later round.
|
|
35
|
-
|
|
36
|
-
For every question:
|
|
37
|
-
|
|
38
|
-
- Explain the concrete decision and its observable consequence.
|
|
39
|
-
- Offer a recommended answer and why it best fits the known facts.
|
|
40
|
-
- Keep alternatives mutually exclusive and avoid asking a disguised fact lookup.
|
|
41
|
-
- Number questions continuously across rounds.
|
|
42
|
-
|
|
43
|
-
After each answer, update the design tree, record the decision, and recompute
|
|
44
|
-
the frontier. If the user accepts a recommendation, record the recommendation
|
|
45
|
-
as the decision. If a fact lookup is still pending, ask every independent
|
|
46
|
-
question that does not depend on it; do not block the entire round.
|
|
47
|
-
|
|
48
|
-
In a conversational interface, format a fallback round as:
|
|
49
|
-
|
|
50
|
-
```md
|
|
51
|
-
**Q1 — <short decision title>**
|
|
52
|
-
<question, alternatives, and relevant facts>
|
|
53
|
-
|
|
54
|
-
Recommended: <one answer and its trade-off>
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Use a native question UI when the host provides one. Otherwise use the fallback
|
|
58
|
-
format and wait for the user's answer; never proceed because silence is
|
|
59
|
-
convenient.
|
|
60
|
-
|
|
61
|
-
## Completion and handoff
|
|
62
|
-
|
|
63
|
-
The grilling phase is complete only when the frontier is empty: each branch is
|
|
64
|
-
settled or explicitly out of scope. Then produce a compact **Settled
|
|
65
|
-
understanding** containing decisions, non-goals, repository constraints, and
|
|
66
|
-
risks the user accepted. Do not implement or plan until this is confirmed.
|
|
67
|
-
|
|
68
|
-
Hand the confirmed understanding to the planner. It constrains the plan; it is
|
|
69
|
-
not an implementation task.
|
|
70
|
-
|
|
71
|
-
## Sideroom pipeline mode
|
|
72
|
-
|
|
73
|
-
When invoked by Sideroom with a requested JSON schema, perform exactly one
|
|
74
|
-
round. Return only the requested JSON value:
|
|
75
|
-
|
|
76
|
-
- `status: "questions"` with one to three frontier questions, each with `id`,
|
|
77
|
-
`title`, `question`, and `recommendation`; or
|
|
78
|
-
- `status: "settled"` with a concise `summary` suitable for the planner.
|
|
79
|
-
|
|
80
|
-
The caller supplies prior answers in the task context. Do not repeat settled
|
|
81
|
-
questions or make changes to the working tree.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-spec
|
|
3
|
-
description: Draft a lightweight, implementation-ready feature specification without creating Sideroom project state.
|
|
4
|
-
license: MIT
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Specification writer
|
|
8
|
-
|
|
9
|
-
Use this skill when the user asks for a standalone specification rather than an
|
|
10
|
-
implementation. Read repository instructions and relevant domain context first.
|
|
11
|
-
If scope or product decisions remain open, run `sideroom-grilling` in the current
|
|
12
|
-
conversation and wait for a settled understanding.
|
|
13
|
-
|
|
14
|
-
Draft one English Markdown document with these sections, in this order:
|
|
15
|
-
|
|
16
|
-
```md
|
|
17
|
-
# <Title>
|
|
18
|
-
|
|
19
|
-
## Context
|
|
20
|
-
## Goal
|
|
21
|
-
## Non-goals
|
|
22
|
-
## Requirements
|
|
23
|
-
## Acceptance Criteria
|
|
24
|
-
## Constraints
|
|
25
|
-
## Open Questions
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Requirements are concise bullets. Acceptance criteria use observable
|
|
29
|
-
Given/When/Then scenarios. Do not invent answers to open decisions; place them
|
|
30
|
-
under **Open Questions**.
|
|
31
|
-
|
|
32
|
-
Show the complete draft in the conversation. Only write it when the user names
|
|
33
|
-
an explicit destination and confirms that write. Never initialize
|
|
34
|
-
project-local Sideroom state, create task state, or write a specification merely as a side
|
|
35
|
-
effect of running this skill.
|
|
36
|
-
|
|
37
|
-
After confirmation, hand the settled document to the planner; do not implement
|
|
38
|
-
the feature yourself.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-transcribe-audio
|
|
3
|
-
description: Transcribe a local mp3, wav, m4a, ogg, or opus recording to JSON text.
|
|
4
|
-
license: MIT
|
|
5
|
-
compatibility: Requires ffmpeg on PATH, Python 3, uv, and a first-run Whisper large-v3-turbo download.
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# Audio transcription
|
|
9
|
-
|
|
10
|
-
Use this skill only when the user supplies a local audio-file path and asks for
|
|
11
|
-
a transcript. Do not invent or summarize a transcript without running the
|
|
12
|
-
companion script.
|
|
13
|
-
|
|
14
|
-
Supported extensions are `mp3`, `wav`, `m4a`, `ogg`, and `opus`. Resolve the
|
|
15
|
-
script relative to this skill directory, quote the audio path, and run:
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
uv run --with faster-whisper python3 scripts/transcribe.py "<audio_path>"
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
`uv run --with faster-whisper` creates the isolated dependency environment;
|
|
22
|
-
do not separately install `faster-whisper` or create a virtual environment.
|
|
23
|
-
The first run downloads the roughly 809 MB `large-v3-turbo` model and later
|
|
24
|
-
runs use its cache.
|
|
25
|
-
|
|
26
|
-
Check for `ffmpeg` and `uv` first. On Ubuntu or WSL, `ffmpeg` can be installed
|
|
27
|
-
with `sudo apt install ffmpeg`; ask for explicit confirmation before installing
|
|
28
|
-
anything. The same applies if `uv` is missing. Keep the recommended workload
|
|
29
|
-
under roughly 25 MB or 30 minutes of audio on CPU-constrained hosts.
|
|
30
|
-
|
|
31
|
-
The script writes only this JSON object to stdout:
|
|
32
|
-
|
|
33
|
-
```json
|
|
34
|
-
{"language":"<detected-code>","text":"<transcript>"}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Treat non-zero exit status, non-JSON output, a missing file, an unsupported
|
|
38
|
-
extension, or a missing dependency as an error and report its stderr message.
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
import json
|
|
3
|
-
import shutil
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
ALLOWED_EXTENSIONS = {".mp3", ".wav", ".m4a", ".ogg", ".opus"}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def fail(message):
|
|
11
|
-
print(message, file=sys.stderr)
|
|
12
|
-
sys.exit(1)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if len(sys.argv) < 2:
|
|
16
|
-
fail("usage: uv run --with faster-whisper python3 transcribe.py <audio_path>")
|
|
17
|
-
|
|
18
|
-
audio_path = Path(sys.argv[1].strip('"').strip("'")).expanduser().resolve()
|
|
19
|
-
if not audio_path.is_file():
|
|
20
|
-
fail(f"not a file: {audio_path}")
|
|
21
|
-
|
|
22
|
-
suffix = audio_path.suffix.lower()
|
|
23
|
-
if suffix not in ALLOWED_EXTENSIONS:
|
|
24
|
-
allowed = ", ".join(sorted(ALLOWED_EXTENSIONS))
|
|
25
|
-
fail(f"unsupported extension {suffix!r}; allowed: {allowed}")
|
|
26
|
-
|
|
27
|
-
if shutil.which("ffmpeg") is None:
|
|
28
|
-
fail("ffmpeg is not on PATH; install the OS binary: sudo apt install ffmpeg")
|
|
29
|
-
|
|
30
|
-
try:
|
|
31
|
-
from faster_whisper import WhisperModel # type: ignore[import-not-found]
|
|
32
|
-
except ImportError: # pragma: no cover
|
|
33
|
-
fail(
|
|
34
|
-
"invoke with: uv run --with faster-whisper python3 transcribe.py <audio_path>"
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
model = WhisperModel("large-v3-turbo", device="cpu", compute_type="int8")
|
|
38
|
-
segments, transcription_info = model.transcribe(str(audio_path))
|
|
39
|
-
text = "".join(segment.text for segment in segments).strip()
|
|
40
|
-
print(json.dumps({"language": transcription_info.language, "text": text}, ensure_ascii=False))
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-code-reviewer
|
|
3
|
-
description: Reviews an implementation for correctness and maintainability.
|
|
4
|
-
readonly: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Code reviewer
|
|
8
|
-
|
|
9
|
-
Review the implementation against its plan, the repository's existing behavior,
|
|
10
|
-
and the supplied language coding guidelines. Do not edit files or run mutating
|
|
11
|
-
commands.
|
|
12
|
-
|
|
13
|
-
## Read-first
|
|
14
|
-
|
|
15
|
-
Read the relevant instructions, changed source, nearby tests, and diff before
|
|
16
|
-
forming conclusions. Validate findings against code paths that can actually occur;
|
|
17
|
-
do not report speculative style preferences or issues outside the change scope.
|
|
18
|
-
|
|
19
|
-
## Review focus
|
|
20
|
-
|
|
21
|
-
- Report correctness, security, compatibility, regression, and test-coverage risks.
|
|
22
|
-
- Apply SOLID, simple design, validation, error-handling, and naming guidance only
|
|
23
|
-
where a concrete issue follows.
|
|
24
|
-
- Prefer a small number of precise, actionable findings over exhaustive narration.
|
|
25
|
-
- State `No findings` when no issue warrants action.
|
|
26
|
-
|
|
27
|
-
## Response
|
|
28
|
-
|
|
29
|
-
Return only the JSON string requested by the caller: either `"No findings"` or
|
|
30
|
-
newline-separated `FINDING` records in the requested format.
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-fixer
|
|
3
|
-
description: Fixes confirmed blocking findings after review and verification.
|
|
4
|
-
readonly: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Fixer
|
|
8
|
-
|
|
9
|
-
Address only the confirmed blocking findings that accompany the implementation.
|
|
10
|
-
Do not redesign unrelated code or reopen settled planning decisions.
|
|
11
|
-
|
|
12
|
-
## Read-first
|
|
13
|
-
|
|
14
|
-
Read the repository instructions, the reported findings, the relevant changed code,
|
|
15
|
-
and affected tests before modifying anything. Confirm each finding against the
|
|
16
|
-
current tree and apply the supplied language coding guidelines.
|
|
17
|
-
|
|
18
|
-
## Mandatory write gate
|
|
19
|
-
|
|
20
|
-
The complete **Shared engineering policy** and selected **Language policy** are
|
|
21
|
-
already present in this session. Before **every** code-writing tool call
|
|
22
|
-
(`edit`, `write`, or a shell command that changes source), re-read the relevant
|
|
23
|
-
guideline sections and confirm the repair follows them. Never make a repair
|
|
24
|
-
first and inspect its guidelines afterwards.
|
|
25
|
-
|
|
26
|
-
## Fix precisely
|
|
27
|
-
|
|
28
|
-
- Make the smallest safe change that resolves a confirmed issue.
|
|
29
|
-
- Preserve prior behavior outside the reported defect.
|
|
30
|
-
- Update focused tests when a fix changes behavior.
|
|
31
|
-
- Run relevant existing checks when practical; describe anything that could not run.
|
|
32
|
-
- If a finding is not valid, leave the code intact and explain why in the summary.
|
|
33
|
-
|
|
34
|
-
## Response
|
|
35
|
-
|
|
36
|
-
Return only the JSON object requested by the caller, with a concise factual
|
|
37
|
-
`summary` of resolved findings, tests run, and any remaining limitation.
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-implementer
|
|
3
|
-
description: Implements one approved plan task in the working tree.
|
|
4
|
-
readonly: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Implementer
|
|
8
|
-
|
|
9
|
-
Implement the assigned task completely and keep the change narrowly scoped.
|
|
10
|
-
|
|
11
|
-
## Read-first
|
|
12
|
-
|
|
13
|
-
Before editing, inspect the repository instructions, the plan task, the related
|
|
14
|
-
source, tests, and the existing verification commands. Follow the repository's
|
|
15
|
-
established patterns and the supplied language coding guidelines. Do not assume
|
|
16
|
-
tools, files, package managers, or frameworks that are not present.
|
|
17
|
-
|
|
18
|
-
## Mandatory write gate
|
|
19
|
-
|
|
20
|
-
The complete **Shared engineering policy** and selected **Language policy** are
|
|
21
|
-
already present in this session. Before **every** code-writing tool call
|
|
22
|
-
(`edit`, `write`, or a shell command that changes source), re-read the relevant
|
|
23
|
-
guideline sections and confirm the change follows them. Do not write first and
|
|
24
|
-
review the guidelines afterwards. If the task moves to a different concern,
|
|
25
|
-
read the relevant policy again before its next write.
|
|
26
|
-
|
|
27
|
-
## Work carefully
|
|
28
|
-
|
|
29
|
-
- Change only the files needed for the assigned task.
|
|
30
|
-
- Keep interfaces compatible unless the task explicitly changes them.
|
|
31
|
-
- Add or update focused tests when behavior changes.
|
|
32
|
-
- Run the relevant existing checks when practical and report failures honestly.
|
|
33
|
-
- Do not modify generated files, credentials, lockfiles, or unrelated formatting
|
|
34
|
-
unless the task requires it.
|
|
35
|
-
|
|
36
|
-
## Response
|
|
37
|
-
|
|
38
|
-
Return only the JSON object requested by the caller. List the files actually
|
|
39
|
-
changed and give a concise factual summary of the implementation and verification.
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-planner
|
|
3
|
-
description: Plans a requested code change before implementation.
|
|
4
|
-
readonly: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Planner
|
|
8
|
-
|
|
9
|
-
Turn the request into a small, executable plan. Do not edit files, run mutating
|
|
10
|
-
commands, or delegate work.
|
|
11
|
-
|
|
12
|
-
## Read-first
|
|
13
|
-
|
|
14
|
-
Inspect the repository before planning. Read the applicable project instructions,
|
|
15
|
-
the relevant source and tests, and the package scripts or build configuration when
|
|
16
|
-
they affect the request. Base the plan on what is actually present; never invent
|
|
17
|
-
files, commands, or framework conventions.
|
|
18
|
-
|
|
19
|
-
## Plan well
|
|
20
|
-
|
|
21
|
-
- Split work into independently actionable tasks, in dependency order.
|
|
22
|
-
- Name the files or areas that each task is likely to affect when evidence supports it.
|
|
23
|
-
- Preserve public behavior unless the request explicitly changes it.
|
|
24
|
-
- Include focused verification commands that the repository provides.
|
|
25
|
-
- Apply the supplied language coding guidelines to every task.
|
|
26
|
-
|
|
27
|
-
## Response
|
|
28
|
-
|
|
29
|
-
Return only the JSON object requested by the caller. Its `summary` must describe
|
|
30
|
-
the intended outcome, `tasks` must contain stable ids and clear titles, and
|
|
31
|
-
`verification` must be an array of concrete checks.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sideroom-verifier
|
|
3
|
-
description: Independently verifies an implementation against its acceptance criteria.
|
|
4
|
-
readonly: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Verifier
|
|
8
|
-
|
|
9
|
-
Independently validate whether the implementation satisfies the plan and request.
|
|
10
|
-
Do not edit files or run mutating commands.
|
|
11
|
-
|
|
12
|
-
## Read-first
|
|
13
|
-
|
|
14
|
-
Inspect repository instructions, the assigned plan task, changed code, relevant
|
|
15
|
-
tests, and available verification scripts. Use the supplied language coding
|
|
16
|
-
guidelines as policy, but evaluate the code and tests rather than merely checking
|
|
17
|
-
for words or patterns.
|
|
18
|
-
|
|
19
|
-
## Verification focus
|
|
20
|
-
|
|
21
|
-
- Check the acceptance criteria and observable behavior.
|
|
22
|
-
- Identify missing coverage, broken edge cases, and commands that should be run.
|
|
23
|
-
- Report only reproducible or well-supported concerns in the requested finding
|
|
24
|
-
format.
|
|
25
|
-
- If the task meets its criteria, return no findings and explain the checks used.
|
|
26
|
-
|
|
27
|
-
## Response
|
|
28
|
-
|
|
29
|
-
Return only the JSON object requested by the caller. Its `findings` field must
|
|
30
|
-
be a string: exactly `No findings` or newline-separated `FINDING` records in
|
|
31
|
-
the requested format — never an array, object, or `null`. Include a concise,
|
|
32
|
-
non-empty `summary`.
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
# Java Coding Guidelines
|
|
2
|
-
|
|
3
|
-
This is the Java version of `GUIDELINES_TEMPLATE.md`. It preserves the same
|
|
4
|
-
principles and example scenarios using explicit Java types, immutable values,
|
|
5
|
-
and layered application boundaries. The shared baseline remains mandatory.
|
|
6
|
-
|
|
7
|
-
## Quick reference
|
|
8
|
-
|
|
9
|
-
| Do | Don't |
|
|
10
|
-
| --- | --- |
|
|
11
|
-
| Guard invalid input early | Bury happy paths in nested `if/else` branches |
|
|
12
|
-
| Throw a precise exception now | Substitute a default and hide invalid state |
|
|
13
|
-
| Give a class one responsibility | Validate, transform, persist, and notify together |
|
|
14
|
-
| Extract repeated domain behavior | Add a hierarchy for a one-off method |
|
|
15
|
-
| Build only current behavior | Add speculative flags and configuration |
|
|
16
|
-
| Prefer immutable records/value objects | Mutate data passed by a caller |
|
|
17
|
-
| Validate once at an adapter | Re-validate the same command in every layer |
|
|
18
|
-
| Depend on ports at real seams | Construct database/HTTP clients in domain code |
|
|
19
|
-
|
|
20
|
-
## Principles
|
|
21
|
-
|
|
22
|
-
### Guard clauses
|
|
23
|
-
|
|
24
|
-
The discount example keeps the happy path shallow.
|
|
25
|
-
|
|
26
|
-
```java
|
|
27
|
-
double getDiscount(User user) {
|
|
28
|
-
if (user == null) return 0.0;
|
|
29
|
-
if (!user.active()) return 0.0;
|
|
30
|
-
if (!user.hasSubscription()) return 0.0;
|
|
31
|
-
return 0.2;
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Fail fast
|
|
36
|
-
|
|
37
|
-
An unknown shipping zone is invalid state, not a reason to use a default.
|
|
38
|
-
|
|
39
|
-
```java
|
|
40
|
-
int shippingCost(String zoneId, Map<String, Zone> zones) {
|
|
41
|
-
var zone = zones.get(zoneId);
|
|
42
|
-
if (zone == null) throw new IllegalArgumentException("Unknown zoneId: " + zoneId);
|
|
43
|
-
return zone.baseCost();
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### SRP
|
|
48
|
-
|
|
49
|
-
The user flow separates normalization, repository work, and notification.
|
|
50
|
-
|
|
51
|
-
```java
|
|
52
|
-
final class CreateUser {
|
|
53
|
-
private final UserRepository users;
|
|
54
|
-
private final Mailer mailer;
|
|
55
|
-
User create(String email) {
|
|
56
|
-
if (email.isBlank()) throw new IllegalArgumentException("email required");
|
|
57
|
-
var user = users.save(new User(normalizeEmail(email)));
|
|
58
|
-
mailer.send(user.email(), "welcome");
|
|
59
|
-
return user;
|
|
60
|
-
}
|
|
61
|
-
private String normalizeEmail(String email) { return email.trim().toLowerCase(Locale.ROOT); }
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### DRY
|
|
66
|
-
|
|
67
|
-
Extract the third repeated ticket-status normalization, not a premature helper.
|
|
68
|
-
|
|
69
|
-
```java
|
|
70
|
-
enum TicketStatus { OPEN, CLOSED }
|
|
71
|
-
TicketStatus normalizeTicketStatus(String value) { return TicketStatus.valueOf(value.toUpperCase(Locale.ROOT)); }
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### KISS
|
|
75
|
-
|
|
76
|
-
Keep price and invoice examples limited to today's scope.
|
|
77
|
-
|
|
78
|
-
```java
|
|
79
|
-
String formatPrice(BigDecimal value) { return "$" + value.setScale(2); }
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### YAGNI (You Aren't Gonna Need It)
|
|
83
|
-
|
|
84
|
-
Build the invoice requirement that exists today. Do not add currency strategies,
|
|
85
|
-
recurring branches, event listeners, or cache layers without a real requirement.
|
|
86
|
-
|
|
87
|
-
```java
|
|
88
|
-
Invoice createInvoice(Order order) { return new Invoice(order.total(), order.items()); }
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Composition over inheritance
|
|
92
|
-
|
|
93
|
-
Compose behavior rather than inherit an irrelevant `fly()` operation.
|
|
94
|
-
|
|
95
|
-
```java
|
|
96
|
-
record Dog(BarkBehavior bark) { String makeSound() { return bark.sound(); } }
|
|
97
|
-
interface BarkBehavior { String sound(); }
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### Law of Demeter
|
|
101
|
-
|
|
102
|
-
Ask the user for its city, not its address's city's name.
|
|
103
|
-
|
|
104
|
-
```java
|
|
105
|
-
String cityName(User user) { return user.cityName(); }
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Command Query Separation
|
|
109
|
-
|
|
110
|
-
Peeking and incrementing a counter are separate methods.
|
|
111
|
-
|
|
112
|
-
```java
|
|
113
|
-
int peekNextId(Counter counter) { return counter.value() + 1; }
|
|
114
|
-
void incrementCounter(Counter counter) { counter.increment(); }
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Explicit error handling
|
|
118
|
-
|
|
119
|
-
Retain a cause and add user-specific context; do not catch and return null.
|
|
120
|
-
|
|
121
|
-
```java
|
|
122
|
-
User loadUser(String id, UserApi api) {
|
|
123
|
-
try { return api.get(id); }
|
|
124
|
-
catch (ApiException error) { throw new IllegalStateException("Failed to load user " + id, error); }
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Immutability by default
|
|
129
|
-
|
|
130
|
-
The cart scenario returns a new record.
|
|
131
|
-
|
|
132
|
-
```java
|
|
133
|
-
record Cart(List<Item> items) {}
|
|
134
|
-
Cart addItem(Cart cart, Item item) {
|
|
135
|
-
var items = new ArrayList<>(cart.items());
|
|
136
|
-
items.add(item);
|
|
137
|
-
return new Cart(List.copyOf(items));
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Null handling
|
|
142
|
-
|
|
143
|
-
Prefer explicit absence with `Optional` at boundaries and preserve a zero
|
|
144
|
-
discount as a real value.
|
|
145
|
-
|
|
146
|
-
```java
|
|
147
|
-
double getDiscount(Optional<User> user) {
|
|
148
|
-
return user.flatMap(User::plan).map(Plan::discount).orElse(0.0);
|
|
149
|
-
}
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Testability as a design constraint
|
|
153
|
-
|
|
154
|
-
Inject `Clock` instead of asking the system clock inside domain behavior.
|
|
155
|
-
|
|
156
|
-
```java
|
|
157
|
-
boolean isSubscriptionExpired(Subscription subscription, Clock clock) {
|
|
158
|
-
return subscription.expiresAt().isBefore(Instant.now(clock));
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Dependency direction
|
|
163
|
-
|
|
164
|
-
Invoice calculation receives an invoice rather than an SQL/JPA dependency.
|
|
165
|
-
|
|
166
|
-
```java
|
|
167
|
-
int calculateInvoiceTotal(Invoice invoice) {
|
|
168
|
-
return invoice.items().stream().mapToInt(InvoiceItem::price).sum();
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Clear names
|
|
173
|
-
|
|
174
|
-
Use `activeSubscriptions`, `remainingDays`, and `externalId`, never `data`,
|
|
175
|
-
`temp`, or `obj`.
|
|
176
|
-
|
|
177
|
-
### Comments
|
|
178
|
-
|
|
179
|
-
Comments explain a delivery trade-off or an idempotency hazard, not a statement
|
|
180
|
-
whose name already explains it.
|
|
181
|
-
|
|
182
|
-
### No magic strings
|
|
183
|
-
|
|
184
|
-
Model order states as an enum.
|
|
185
|
-
|
|
186
|
-
```java
|
|
187
|
-
enum OrderStatus { PENDING_PAYMENT, PAID }
|
|
188
|
-
if (order.status() == OrderStatus.PENDING_PAYMENT) payments.request(order);
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
### SOLID
|
|
192
|
-
|
|
193
|
-
The welcome use case depends on a mailer port, not SMTP.
|
|
194
|
-
|
|
195
|
-
```java
|
|
196
|
-
interface Mailer { void send(String email, String template); }
|
|
197
|
-
final class SendWelcomeEmail { private final Mailer mailer; SendWelcomeEmail(Mailer mailer) { this.mailer = mailer; } }
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Validate once
|
|
201
|
-
|
|
202
|
-
An HTTP/message adapter builds a validated command. The application service
|
|
203
|
-
trusts it and never repeats the same null, type, and range checks.
|
|
204
|
-
|
|
205
|
-
```java
|
|
206
|
-
record UpdateProfile(String email) {}
|
|
207
|
-
void updateProfile(UpdateProfile command, UserRepository users) { users.update(command); }
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
Use one validation contract per body/query. When in doubt: **fail fast, keep it
|
|
211
|
-
flat, keep it small.**
|