@erclx/aitk 0.88.0 → 0.89.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-orchestrate/SKILL.md +5 -3
- package/claude/skills/claude-worktree/REQUIREMENT.md +8 -0
- package/claude/skills/claude-worktree/SKILL.md +22 -0
- package/claude/skills/decision-escalate/REQUIREMENT.md +45 -0
- package/claude/skills/decision-escalate/SKILL.md +81 -0
- package/docs/agents/markdown-audit.md +1 -1
- package/docs/ai-workflow.md +1 -0
- package/docs/operating-model.md +4 -2
- package/package.json +1 -1
- package/standards/index.md +2 -2
- package/standards/markdown.md +7 -3
- package/standards/prose.md +10 -4
- package/tooling/astro/configs/astro.config.mjs +11 -0
- package/tooling/astro/configs/playwright.config.ts +4 -3
- package/tooling/astro/manifest.toml +5 -3
- package/tooling/astro/reference.md +3 -3
- package/tooling/claude/seeds/CLAUDE.md +7 -3
- package/tooling/vite-react/configs/playwright.config.ts +4 -3
- package/tooling/vite-react/configs/vite.config.ts +10 -0
- package/tooling/vite-react/manifest.toml +4 -2
- package/tooling/vite-react/reference.md +4 -4
- package/tooling/web/configs/scripts/worktree-port.sh +36 -0
- package/tooling/web/manifest.toml +3 -3
- package/tooling/web/reference.md +12 -0
|
@@ -196,6 +196,8 @@ properly and stop when you cannot.
|
|
|
196
196
|
Serialize any track that touches a shared wiring seam with another in flight.
|
|
197
197
|
Merge the branch with the smallest shared-file footprint first, and merge a
|
|
198
198
|
branch touching `CLAUDE.md`, a Claude context entry, or a regenerated `index.md`
|
|
199
|
-
last. Have every sibling rebase on the new `main` before the next merge.
|
|
200
|
-
a
|
|
201
|
-
|
|
199
|
+
last. Have every sibling rebase on the new `main` before the next merge. Two
|
|
200
|
+
workers running a server take a port apiece without being told to, since a
|
|
201
|
+
stack derives it from the worktree it runs in through `scripts/worktree-port.sh`.
|
|
202
|
+
Read that value rather than assigning one, and set `WORKTREE_PORT_OFFSET` by
|
|
203
|
+
hand only when two worktrees derive the same offset.
|
|
@@ -11,6 +11,10 @@ Without this skill, the user names the worktree by hand, and a name matching no
|
|
|
11
11
|
|
|
12
12
|
Entry also writes the bare flag into the shared config, which strands the main worktree. Every command run there fails while the files sit untouched on disk, and the linked worktree keeps working, so nothing surfaces until the operator returns to the main checkout and finds the repository broken. A rename onto a branch that already exists is the third failure, and it is the one that destroys work rather than blocking it.
|
|
13
13
|
|
|
14
|
+
The tree the entry hands over is also not one the session can run. Dependencies live in a folder git ignores and never shares between working directories, so a fresh worktree arrives with none, and nothing on the entry path says so. The session finds out from whichever command needs them first, and the message it gets names a missing module rather than an empty working directory.
|
|
15
|
+
|
|
16
|
+
A stack that derives its ports from the working directory has the same shape. The number is correct and invisible, and `claude-orchestrate` sends a reader here to read it rather than assign one, so the entry that knows the working directory is the surface that owes it.
|
|
17
|
+
|
|
14
18
|
## Must
|
|
15
19
|
|
|
16
20
|
- Derive the name from the plan matched to the current branch, falling through the ordered sources rather than picking
|
|
@@ -21,9 +25,13 @@ Entry also writes the bare flag into the shared config, which strands the main w
|
|
|
21
25
|
- Test the target branch and the target directory before entering, so a stop costs no worktree
|
|
22
26
|
- Read the bare flag before writing it, and repair it on both sides of entry
|
|
23
27
|
- Announce the repair only when a write actually happened
|
|
28
|
+
- Report on one line whether the entered worktree carries its dependencies, naming the command that installs them when it does not
|
|
29
|
+
- Report the same way when the project declares no package manifest, since silence there reads as a passing check
|
|
30
|
+
- Report the port offset this worktree derives, and say so when the project installs no derivation
|
|
24
31
|
|
|
25
32
|
## Must not
|
|
26
33
|
|
|
34
|
+
- Install dependencies on the session's behalf. Entering a worktree to read is as common as entering one to run, and an install is slow and needs a network.
|
|
27
35
|
- Pick between plans when more than one could match. Ask.
|
|
28
36
|
- Enter on a name inferred from session context without confirmation
|
|
29
37
|
- Delete or overwrite a branch or a worktree directory that already carries the target name
|
|
@@ -96,4 +96,26 @@ git config core.bare false
|
|
|
96
96
|
|
|
97
97
|
The flag is not set on every entry, so read before writing and announce only when the write happened. Tracked upstream as `anthropics/claude-code#58345`, closed as not planned, so the repair stays until the tool changes.
|
|
98
98
|
|
|
99
|
+
## Step 6: report whether the tree can run
|
|
100
|
+
|
|
101
|
+
A linked worktree is a second working directory over one repository, and every ecosystem installs its dependencies into a folder git ignores. Nothing copies that folder across, so a fresh worktree arrives without it and the session learns as much from whichever command needs it first, which reports a missing module rather than an empty working directory.
|
|
102
|
+
|
|
103
|
+
Report the state on one line. Do not install. Entering a worktree to read is as common as entering one to run, and an install is slow, needs a network, and picks an ecosystem on the session's behalf.
|
|
104
|
+
|
|
105
|
+
Read the worktree root and emit the first line that matches:
|
|
106
|
+
|
|
107
|
+
- `package.json` present, `node_modules/` missing: `Dependencies are not installed. Run <install> before any build, test, or server command.` Take `<install>` from the lockfile beside the manifest, and use `bun install` when no lockfile names one.
|
|
108
|
+
- A `pyproject.toml` or `requirements.txt` present, `.venv/` missing: `No virtual environment. Create and populate one before running anything.`
|
|
109
|
+
- A manifest present with its folder alongside it: `Dependencies are installed.`
|
|
110
|
+
- No package manifest of either kind: `No package manifest, so there is nothing to install.`
|
|
111
|
+
|
|
112
|
+
The last line is what keeps the step honest on a stack this skill cannot read. Entry is not stack-aware, and silence is indistinguishable from a check that passed.
|
|
113
|
+
|
|
114
|
+
Then report the port this worktree derives, on a second line:
|
|
115
|
+
|
|
116
|
+
- `scripts/worktree-port.sh` present: run `bash scripts/worktree-port.sh` and emit `Port offset <n>. Every served port adds it to the stack default.`
|
|
117
|
+
- Absent: `No port derivation installed, so every served port is the stack default.`
|
|
118
|
+
|
|
119
|
+
The offset is what `claude-orchestrate` sends a reader here to read rather than assign, and what an operator overrides through `WORKTREE_PORT_OFFSET` when two worktrees derive the same value. Deriving it correctly and printing it nowhere leaves both instructions naming a number no surface emits.
|
|
120
|
+
|
|
99
121
|
Do not invoke `ExitWorktree` from this skill. Exit is the user's call.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decision-escalate
|
|
3
|
+
description: Scope boundary for handing a decision back to the operator, and the batching contract that keeps the handoff to one turn
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Decision escalate requirement
|
|
7
|
+
|
|
8
|
+
## Gap
|
|
9
|
+
|
|
10
|
+
Without this skill, a session facing a decision that belongs to the operator asks about it one question at a time across several turns, so the operator answers each without knowing how many are open and cannot trade one against another. It asks in prose with no options attached, so the operator has to invent the alternatives before picking one. It names an option and omits what the other one buys, so the pick is made blind. It escalates a call it could have settled from the repository or from its own judgment, which spends the operator's attention on work the session owed.
|
|
11
|
+
|
|
12
|
+
It also empties a long backlog into one turn, so the questions past the first few arrive in no order and the operator cannot tell which ones the work is actually stalled on. It carries on with work the unanswered questions govern, so an answer arrives against output already built the other way. It takes the answer, acts, and leaves the plan, task, or record that posed the question reading as open, so the next session re-asks a decision the operator already made.
|
|
13
|
+
|
|
14
|
+
A body written around a named tool fails a third way. A session on a surface carrying no structured question tool reads an instruction it cannot execute and falls back to whatever it would have done unaided, which is the per-decision ask this file exists against.
|
|
15
|
+
|
|
16
|
+
The opposite failure is the one that ships silently. A session that takes a preference decision itself produces work the operator did not ask for and never learns a choice was made, because nothing in the output says a fork was passed.
|
|
17
|
+
|
|
18
|
+
`CLAUDE.md` states both branches of the rule, sending an ordinary judgment call to a pick with the tradeoff in one sentence and a preference-deciding call to the operator. The first branch has `snippets/decision-help.md` behind it and the second had no surface at all.
|
|
19
|
+
|
|
20
|
+
## Must
|
|
21
|
+
|
|
22
|
+
- Collect every open decision before asking any of them, so the operator sees the set rather than the first one
|
|
23
|
+
- Put the whole batch in one turn, since a batch split across turns is the per-decision ask this skill exists to replace
|
|
24
|
+
- Give each question two to four options, each carrying what it means and what it costs, with the recommendation ranked first and named as such
|
|
25
|
+
- State the behavior first and the tool second, so the body runs on a chat surface carrying no structured question tool
|
|
26
|
+
- Cap a batch at four questions and say how many are held, since a structured question tool takes four and an uncapped batch hides the overflow
|
|
27
|
+
- Hold every escalated decision until it is answered, and continue only the work depending on none of them
|
|
28
|
+
- Route a pick that changes a written artifact into that artifact under the standard owning it, rather than leaving it in the session
|
|
29
|
+
|
|
30
|
+
## Must not
|
|
31
|
+
|
|
32
|
+
- Answer on the operator's behalf, or treat the recommendation as the answer because no reply arrived
|
|
33
|
+
- Escalate a decision the session can settle from its own judgment or from reading the repository, which is the first branch of the rule and belongs to the session
|
|
34
|
+
- Escalate a decision already taken, which goes to the record that holds it rather than back to the operator
|
|
35
|
+
|
|
36
|
+
## Guards
|
|
37
|
+
|
|
38
|
+
- Nothing open, stop rather than manufacturing a question to justify the invocation
|
|
39
|
+
|
|
40
|
+
## Out of scope
|
|
41
|
+
|
|
42
|
+
- Making the ordinary judgment call, which is a pick plus a one-sentence tradeoff and needs no surface
|
|
43
|
+
- The chat-side pick with no repository behind it, which `snippets/decision-help.md` covers and reaches a different reader
|
|
44
|
+
- Writing the decision into a plan, task, or architecture record, which each owning standard governs and this skill only routes to
|
|
45
|
+
- Deciding when to fire. The skill is user-invoked through `disable-model-invocation`, so escalating is the operator's call rather than a description match.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: decision-escalate
|
|
3
|
+
description: Collects every open decision whose answer turns on the operator's preference, puts them as one batch of questions each carrying options and a recommended default, then waits. Use when asked to "escalate this", "ask me the open questions", "batch the open decisions", "what do you need from me", or "stop and ask before you pick". Do NOT use for a judgment call the session can settle, which is a pick with the tradeoff stated in one sentence.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Decision escalate
|
|
8
|
+
|
|
9
|
+
Put every open decision that belongs to the operator as one batch, each carrying its options and a recommended default, then stop until the batch is answered.
|
|
10
|
+
|
|
11
|
+
One batch is the whole point. A session that asks one question, acts, then asks the next spends the operator's attention once per decision and hides how many are open.
|
|
12
|
+
|
|
13
|
+
## Guards
|
|
14
|
+
|
|
15
|
+
- If nothing is open, stop: `❌ Nothing to escalate. Every open call is one this session can make.`
|
|
16
|
+
- Escalate only a decision whose answer turns on the operator's preference. A judgment call with two or three reasonable options the session can weigh is a pick with the tradeoff stated in one sentence, taken without asking.
|
|
17
|
+
- Escalate only what is open now. A decision already taken this session goes to the artifact that records it rather than back to the operator.
|
|
18
|
+
- Do not act on any decision in the batch before it is answered. Continue the work that depends on none of them.
|
|
19
|
+
- Do not escalate a question the session can answer by reading the repository. Read first, and escalate what the tree does not settle.
|
|
20
|
+
|
|
21
|
+
## Step 1: collect what is open
|
|
22
|
+
|
|
23
|
+
Sweep the session for every decision still unmade. The usual sources:
|
|
24
|
+
|
|
25
|
+
- A plan question whose answer turns on preference rather than measurement
|
|
26
|
+
- A fork the session parked to keep moving, where both branches still ship
|
|
27
|
+
- A default the session took silently that changes what the operator receives
|
|
28
|
+
- A scope boundary the request left ambiguous, where the two readings produce different work
|
|
29
|
+
|
|
30
|
+
Drop anything the session can settle. What survives is the batch.
|
|
31
|
+
|
|
32
|
+
## Step 2: shape each question
|
|
33
|
+
|
|
34
|
+
Each entry carries a short header naming the axis, the question itself, and two to four options. Give every option what it means and what it costs. Rank the recommendation first and say it is the recommendation.
|
|
35
|
+
|
|
36
|
+
An option with no stated cost is not an option, since the operator picks it without knowing what the other one buys.
|
|
37
|
+
|
|
38
|
+
Cap the batch at four. A structured question tool takes four, and a batch past that is a session asking to be redesigned rather than answered. When more than four are open, send the four blocking the most work and say in one line how many are held.
|
|
39
|
+
|
|
40
|
+
## Step 3: put the batch
|
|
41
|
+
|
|
42
|
+
Put every question in one turn. Never split the batch across turns and never ask the first while the rest stay unstated.
|
|
43
|
+
|
|
44
|
+
When the session runs on a surface carrying a structured question tool, such as `AskUserQuestion` in Claude Code, send the whole batch through one call with one entry per decision. The tool renders the options and collects the picks together.
|
|
45
|
+
|
|
46
|
+
Otherwise write the batch as a numbered list in one message, each question followed by its lettered options with the recommendation marked. The behavior is the same on either surface, and only the rendering changes.
|
|
47
|
+
|
|
48
|
+
Batch shape:
|
|
49
|
+
|
|
50
|
+
```plaintext
|
|
51
|
+
<N> open decisions. <M> held.
|
|
52
|
+
|
|
53
|
+
1. <axis>: <question>
|
|
54
|
+
a. <option> (recommended): <what it means>, <what it costs>
|
|
55
|
+
b. <option>: <what it means>, <what it costs>
|
|
56
|
+
|
|
57
|
+
2. <axis>: <question>
|
|
58
|
+
a. <option> (recommended): <what it means>, <what it costs>
|
|
59
|
+
b. <option>: <what it means>, <what it costs>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Step 4: wait
|
|
63
|
+
|
|
64
|
+
Stop after the batch. Do not answer on the operator's behalf, do not act on the recommendation because it is the recommendation, and do not fill the wait with work the batch would invalidate.
|
|
65
|
+
|
|
66
|
+
An operator who answers some and not others has answered those. Continue on the answered ones and hold the rest.
|
|
67
|
+
|
|
68
|
+
## Step 5: record and continue
|
|
69
|
+
|
|
70
|
+
Restate each pick in one line, then continue the work.
|
|
71
|
+
|
|
72
|
+
A pick that changes a written artifact goes into that artifact under the rule its own standard sets. An answer to an open plan question rewrites that question's `- Suggested:` line to the pick and names the operator as its source, leaving the `- Answer:` slot blank, per `.claude/standards/plan.md`, or `${CLAUDE_SKILL_DIR}/../../standards/plan.md` when the project does not have it. The standard bars a session from filling that slot even when the operator supplied the pick, and the blank slot resolves to the rewritten suggestion, so the record carries the operator's decision either way. A pick that settles nothing written stays in the session.
|
|
73
|
+
|
|
74
|
+
Output after the answers land:
|
|
75
|
+
|
|
76
|
+
```plaintext
|
|
77
|
+
✅ <N> decisions answered
|
|
78
|
+
<axis>: <pick>
|
|
79
|
+
<axis>: <pick>
|
|
80
|
+
<M held, restated in one line each>
|
|
81
|
+
```
|
|
@@ -51,7 +51,7 @@ A banned word is bounded on a word character or a hyphen either side. A plain wo
|
|
|
51
51
|
|
|
52
52
|
A banned spelling keeps the plain word boundary, hyphens included. The two bans target different things: a word ban targets the word, so reading a compound as one word is correct, while a spelling ban targets the orthography inside it, which sits in `behaviour-driven` as plainly as it sits alone.
|
|
53
53
|
|
|
54
|
-
Two ban shapes stay unmeasured and the report says so on every run. A
|
|
54
|
+
Two ban shapes stay unmeasured and the report says so on every run. A multi-word ban escapes the harvest by width, whether it carries a placeholder standing in for the rest of the sentence or spells the phrase out in full, and every rule under `## Voice` is a judgment. The bans `## Language` states over what a sentence may claim sit in the first group by construction, since a literal match over a pattern reports the compliant text and reaches none of the violations. A report listing hits without naming those would read as a verdict on the whole standard.
|
|
55
55
|
|
|
56
56
|
### Bullets, paragraphs, and depth
|
|
57
57
|
|
package/docs/ai-workflow.md
CHANGED
|
@@ -169,6 +169,7 @@ Before the first feature session on a UI-heavy project, pick a design tier. The
|
|
|
169
169
|
| `aitk:claude-intake` | File a brain dump into an inventory under `.claude/intake/`, one item per finding with a verdict |
|
|
170
170
|
| `aitk:claude-groundwork` | Before a plan is warranted, measure an unknown in a track folder under `.claude/groundwork/` |
|
|
171
171
|
| `aitk:claude-feature` | Before implementation, scan for conflicts and ambiguities |
|
|
172
|
+
| `aitk:decision-escalate` | Batch every open decision that turns on your preference into one set of questions, each with a recommended default |
|
|
172
173
|
| `aitk:claude-roadmap` | Sequence MVP scope into ordered versions in `.claude/ROADMAP.md` |
|
|
173
174
|
| `aitk:claude-orchestrate` | Assert the orchestrator role, refill the ready queue, and dispatch the feature, review, and worktree skills |
|
|
174
175
|
| `aitk:claude-diagram` | Draft per-kind mermaid entries under `.claude/diagrams/` from architecture and code, then verify each rendered image |
|
package/docs/operating-model.md
CHANGED
|
@@ -125,8 +125,10 @@ at two or three worker tracks and split them across the stack so they do not
|
|
|
125
125
|
collide on the same files.
|
|
126
126
|
|
|
127
127
|
Unit checks run freely in many worktrees at once.
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
A dev server, an end-to-end run, and a screenshot run alongside each other on a
|
|
129
|
+
web stack, since every worktree derives its own port. Singleton resources (one
|
|
130
|
+
local model server, one GPU) still serialize, as does any port a stack fixes by
|
|
131
|
+
hand. See
|
|
130
132
|
[Claude Code and git worktrees](../wiki/claude/claude-worktrees.md) for merge order and the
|
|
131
133
|
port-collision detail.
|
|
132
134
|
|
package/package.json
CHANGED
package/standards/index.md
CHANGED
|
@@ -13,10 +13,10 @@ Reference docs for consistent authoring across the toolkit and target projects.
|
|
|
13
13
|
- [Diagram reference](diagrams.md): Shape and content rules for .claude/diagrams/<kind>.md files
|
|
14
14
|
- [Groundwork reference](groundwork.md): Folder layout, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
|
|
15
15
|
- [Intake reference](intake.md): Folder layout, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
|
|
16
|
-
- [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, punctuation, emphasis, and file references
|
|
16
|
+
- [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, and file references
|
|
17
17
|
- [Memory reference](memory.md): Filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire
|
|
18
18
|
- [Plan reference](plan.md): Filename and slug, required sections, the suggested-and-answer contract, and the lifecycle from the live folder to the archive
|
|
19
|
-
- [Prose reference](prose.md): Voice, language, and frontmatter wording for reference markdown
|
|
19
|
+
- [Prose reference](prose.md): Voice, language, what prose may claim, and frontmatter wording for reference markdown
|
|
20
20
|
- [Publish reference](publish.md): Scan an author runs against finished text, the cross-reference form each destination takes, and the response to an unreadable source
|
|
21
21
|
- [Readme reference](readme.md): Readme voice, structure, and content conventions
|
|
22
22
|
- [Requirements reference](requirements.md): Shape and content rules for .claude/REQUIREMENTS.md
|
package/standards/markdown.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Markdown reference
|
|
3
|
-
description: Headings, paragraph and list structure, code spans, punctuation, emphasis, and file references
|
|
3
|
+
description: Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, and file references
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Markdown reference
|
|
@@ -9,11 +9,11 @@ Applies to markdown reference docs, READMEs, and inline documentation in repos.
|
|
|
9
9
|
|
|
10
10
|
## Scope
|
|
11
11
|
|
|
12
|
-
Governs the markdown mechanics of every markdown file: headings, paragraph and list structure, code spans and fences, punctuation, emphasis, and file references. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, and it carries no template because mechanics are written across every document and have no shape of their own.
|
|
12
|
+
Governs the markdown mechanics of every markdown file: headings, paragraph and list structure, code spans and fences, the form a date takes, punctuation, emphasis, and file references. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, and it carries no template because mechanics are written across every document and have no shape of their own.
|
|
13
13
|
|
|
14
14
|
Does not govern:
|
|
15
15
|
|
|
16
|
-
- Voice, word choice, and the wording of a `title` or `description`: `prose.md`
|
|
16
|
+
- Voice, word choice, what prose may claim, and the wording of a `title` or `description`: `prose.md`
|
|
17
17
|
- What sections a document has, or what belongs in each: the standard for that document type
|
|
18
18
|
- The text inside a fenced block, which follows the conventions of its own language rather than these
|
|
19
19
|
- The scan that applies the punctuation bans to finished text on its way out: `publish.md`
|
|
@@ -49,6 +49,10 @@ Does not govern:
|
|
|
49
49
|
- Use a language identifier on all fenced code blocks (`markdown`, `typescript`, `plaintext`). Never use a bare ` ``` `
|
|
50
50
|
- In ASCII tree diagrams, use `←` for inline annotations. Never use `#`.
|
|
51
51
|
|
|
52
|
+
## Dates
|
|
53
|
+
|
|
54
|
+
- Write a date as `YYYY-MM-DD` wherever one appears, in frontmatter, in prose, and in a filename. Never a month name, a slash-separated form, or a two-digit year.
|
|
55
|
+
|
|
52
56
|
## Punctuation
|
|
53
57
|
|
|
54
58
|
- Do not use em dashes (`—`) or semicolons (`;`). Rewrite or restructure the sentence to avoid them.
|
package/standards/prose.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Prose reference
|
|
3
|
-
description: Voice, language, and frontmatter wording for reference markdown
|
|
3
|
+
description: Voice, language, what prose may claim, and frontmatter wording for reference markdown
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Prose reference
|
|
@@ -11,11 +11,11 @@ The yield covers voice alone. The language rules below stay in force on every su
|
|
|
11
11
|
|
|
12
12
|
## Scope
|
|
13
13
|
|
|
14
|
-
Governs voice, word choice, and frontmatter wording wherever prose is written. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, yields on voice alone where that standard states one, and carries no template because voice is written across every document and has none of its own to shape.
|
|
14
|
+
Governs voice, word choice, what prose may claim about its subject and its sources, and frontmatter wording wherever prose is written. It is an attribute standard rather than a document-type one, so it applies over documents whose shape another standard sets, yields on voice alone where that standard states one, and carries no template because voice is written across every document and has none of its own to shape.
|
|
15
15
|
|
|
16
16
|
Does not govern:
|
|
17
17
|
|
|
18
|
-
- Headings, list and paragraph structure, code spans, punctuation, emphasis, and file references: `markdown.md`
|
|
18
|
+
- Headings, list and paragraph structure, code spans, the form a date takes, punctuation, emphasis, and file references: `markdown.md`
|
|
19
19
|
- What sections a document has, or what belongs in each: the standard for that document type
|
|
20
20
|
- Which frontmatter fields a document carries, which is that standard's own subject. This file governs the wording of a `title` and a `description` and nothing else about them.
|
|
21
21
|
- Phase-label and semver discipline: `versioning.md`
|
|
@@ -44,8 +44,14 @@ Does not govern:
|
|
|
44
44
|
- Do not pad verb phrases or delay the action. Write the shortest form (`in order to` → `to`, `ensure that X is set` → `set X`, `By doing X, you can Y` → state Y directly).
|
|
45
45
|
- Do not address the reader as a participant (`Let's`, `Here's`, `Here are`). State the content directly.
|
|
46
46
|
- Commit to a position. Do not hedge in clusters (`It might be worth considering`) or use false balance (`While X is true, Y is also important`). Recommend, or state the tradeoff.
|
|
47
|
+
- Do not inflate significance. State what a thing does rather than calling it `a major milestone` or `a turning point for the field`.
|
|
48
|
+
- Do not name a person, company, or product to borrow its authority. Name a source only where the claim turns on who made it.
|
|
49
|
+
- Do not attribute a claim to an unnamed authority (`experts say`, `studies show`, `it is widely believed`). Name the source or cut the claim.
|
|
50
|
+
- Do not introduce a fact, name, date, or citation the source does not carry when rewriting existing text. A rewrite changes wording and never claims.
|
|
47
51
|
|
|
48
|
-
The character bans sit in `markdown.md` under `## Punctuation` rather than here, because an em dash and a semicolon are typography and
|
|
52
|
+
The character bans sit in `markdown.md` under `## Punctuation` rather than here, because an em dash and a semicolon are typography and the bans here reach the words a sentence chooses and the claims it makes. A surface applying both reads both files.
|
|
53
|
+
|
|
54
|
+
Illustrate a pattern ban with a multi-word phrase. An audit reading this section harvests the single lowercase backticked words out of every `- Do not use ` bullet into a literal ban set, so a one-word example bans that word everywhere it appears rather than banning the pattern it stands for. Both the toolkit command and the audit hook a project installs parse that shape, so the constraint holds wherever this file lands.
|
|
49
55
|
|
|
50
56
|
## Frontmatter descriptions
|
|
51
57
|
|
|
@@ -3,9 +3,14 @@ import tailwindcss from '@tailwindcss/vite'
|
|
|
3
3
|
import { defineConfig } from 'astro/config'
|
|
4
4
|
import path from 'path'
|
|
5
5
|
|
|
6
|
+
const portOffset = Number(process.env.WORKTREE_PORT_OFFSET) || 0
|
|
7
|
+
|
|
6
8
|
export default defineConfig({
|
|
7
9
|
integrations: [react()],
|
|
8
10
|
site: process.env.ASTRO_SITE,
|
|
11
|
+
server: {
|
|
12
|
+
port: 4321 + portOffset,
|
|
13
|
+
},
|
|
9
14
|
vite: {
|
|
10
15
|
plugins: [tailwindcss()],
|
|
11
16
|
resolve: {
|
|
@@ -13,5 +18,11 @@ export default defineConfig({
|
|
|
13
18
|
'@': path.resolve('./src'),
|
|
14
19
|
},
|
|
15
20
|
},
|
|
21
|
+
server: {
|
|
22
|
+
strictPort: true,
|
|
23
|
+
},
|
|
24
|
+
preview: {
|
|
25
|
+
strictPort: true,
|
|
26
|
+
},
|
|
16
27
|
},
|
|
17
28
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineConfig, devices } from '@playwright/test'
|
|
2
2
|
|
|
3
3
|
const isCI = !!process.env.CI
|
|
4
|
+
const baseURL = `http://localhost:${4321 + (Number(process.env.WORKTREE_PORT_OFFSET) || 0)}`
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
7
|
testDir: 'e2e',
|
|
@@ -10,7 +11,7 @@ export default defineConfig({
|
|
|
10
11
|
reporter: isCI ? 'list' : 'html',
|
|
11
12
|
use: {
|
|
12
13
|
trace: 'on-first-retry',
|
|
13
|
-
baseURL
|
|
14
|
+
baseURL,
|
|
14
15
|
},
|
|
15
16
|
projects: [
|
|
16
17
|
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
@@ -19,7 +20,7 @@ export default defineConfig({
|
|
|
19
20
|
],
|
|
20
21
|
webServer: {
|
|
21
22
|
command: 'bun run build && bun run preview',
|
|
22
|
-
url:
|
|
23
|
-
reuseExistingServer:
|
|
23
|
+
url: baseURL,
|
|
24
|
+
reuseExistingServer: false,
|
|
24
25
|
},
|
|
25
26
|
})
|
|
@@ -15,15 +15,17 @@ packages = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
[scripts]
|
|
18
|
-
"dev" = "astro dev"
|
|
18
|
+
"dev" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) astro dev"
|
|
19
19
|
"build" = "astro check && astro build"
|
|
20
|
-
"preview" = "astro preview"
|
|
20
|
+
"preview" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) astro preview"
|
|
21
21
|
"astro" = "astro"
|
|
22
22
|
"typecheck" = "astro check"
|
|
23
23
|
"setup" = "./scripts/setup.sh"
|
|
24
24
|
|
|
25
25
|
[scripts.override]
|
|
26
|
-
"screenshot" = "PREVIEW_PORT
|
|
26
|
+
"screenshot" = "PREVIEW_PORT=$(bash scripts/worktree-port.sh 4321) bash scripts/screenshot.sh"
|
|
27
|
+
"dev" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) astro dev"
|
|
28
|
+
"preview" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) astro preview"
|
|
27
29
|
|
|
28
30
|
[gitignore]
|
|
29
31
|
"# Astro" = [".astro/"]
|
|
@@ -17,9 +17,9 @@ The astro stack covers Astro + TypeScript projects: content sites, marketing sit
|
|
|
17
17
|
|
|
18
18
|
## What ships as golden configs
|
|
19
19
|
|
|
20
|
-
- `astro.config.mjs`: `@astrojs/react` integration, `@tailwindcss/vite` in `vite.plugins`, `@/` path alias via `vite.resolve.alias`, `ASTRO_SITE` env for the `site` field.
|
|
20
|
+
- `astro.config.mjs`: `@astrojs/react` integration, `@tailwindcss/vite` in `vite.plugins`, `@/` path alias via `vite.resolve.alias`, `ASTRO_SITE` env for the `site` field. Port `4321` plus `WORKTREE_PORT_OFFSET` at `server.port`, with `strictPort` under `vite.server` and `vite.preview`. Astro merges the user's `vite` block into the config backing both its dev and its static preview server, and feeds `server.port` through as the preview port, so the port sits at the top level while the bind guarantee sits under `vite`.
|
|
21
21
|
- `vitest.config.ts`: uses `getViteConfig` from `astro/config` (not `mergeConfig`). jsdom, globals, setup file, `passWithNoTests: true`, v8 coverage, `**/*.astro` in coverage excludes.
|
|
22
|
-
- `playwright.config.ts`: all browsers, `webServer` runs `bun run build && bun run preview` on port 4321
|
|
22
|
+
- `playwright.config.ts`: all browsers, `webServer` runs `bun run build && bun run preview` on port `4321` plus `WORKTREE_PORT_OFFSET`, `reuseExistingServer: false`. Astro's dev/prod gap is wide (MDX, island hydration, asset optimization), so E2E always tests the built `dist/`.
|
|
23
23
|
- `tsconfig.json`: extends `astro/tsconfigs/strict`, adds `skipLibCheck`, `vitest/globals` and `@testing-library/jest-dom` in types, `@/` paths.
|
|
24
24
|
- `eslint.config.js`: overrides the web layer. Adds `eslint-plugin-astro` (`.astro` parser via `astro-eslint-parser`). React-hooks scoped to `.jsx`/`.tsx` only (`.astro` is not React). `src/pages/**` exempt from filename and folder naming conventions because Astro's file-based routing ties names to URL segments.
|
|
25
25
|
|
|
@@ -49,7 +49,7 @@ Add `prettier-plugin-astro` first in plugins, then `prettier-plugin-tailwindcss`
|
|
|
49
49
|
|
|
50
50
|
Append to the `## Scripts` table:
|
|
51
51
|
|
|
52
|
-
| `bun run dev` | Start the Astro dev server on port 4321. |
|
|
52
|
+
| `bun run dev` | Start the Astro dev server on port 4321, plus this worktree's port offset. |
|
|
53
53
|
| `bun run build` | Run `astro check` then build the static output. |
|
|
54
54
|
| `bun run preview` | Serve the built site locally. |
|
|
55
55
|
| `bun run astro` | Expose the Astro CLI. |
|
|
@@ -36,11 +36,15 @@
|
|
|
36
36
|
|
|
37
37
|
## Output
|
|
38
38
|
|
|
39
|
-
- After creating or modifying a file, include its path on its own line so
|
|
39
|
+
- After creating or modifying a file, include its path on its own line so the reader can open it. Do not paraphrase paths into prose ("the seeds folder", "your CLAUDE.md").
|
|
40
|
+
- Read `CLAUDE_CODE_ENTRYPOINT` once, at the first response that emits a path, and reuse it for the rest of the session. The surface cannot change mid-session, so a second read only confirms the first.
|
|
41
|
+
- When it reads `claude-desktop`, emit each path as a markdown link carrying the path as its text and an absolute `file://` URI as its target, resolving a relative path against the main project root to build that target. The desktop file tree hides dotted folders, so a bare path into one names a file the reader cannot reach.
|
|
42
|
+
- On every other value, including unset, emit the path bare. A terminal emulator makes it clickable through its own path detection, and link markup defeats that.
|
|
43
|
+
- Both forms govern a path emitted in a response. A path written into a markdown file follows `.claude/standards/markdown.md` instead, which backticks a file reference and never repeats it as a link label.
|
|
40
44
|
- Use the path the user's editor can resolve. The editor is rooted at the main project root.
|
|
41
|
-
- In the main worktree: relative from `pwd` works because `pwd` equals the editor root
|
|
45
|
+
- In the main worktree: relative from `pwd` works because `pwd` equals the editor root.
|
|
42
46
|
- In a linked worktree (under `.claude/worktrees/<name>/`): use absolute paths. Relative paths from worktree `pwd` would not resolve against the editor's project root.
|
|
43
|
-
- When the response covers multiple files, group paths under headers: `**Created:**`, `**Modified:**`, `**Deleted:**`. For single-file changes, the path on its own line is enough.
|
|
47
|
+
- When the response covers multiple files, group paths under headers: `**Created:**`, `**Modified:**`, `**Deleted:**`. Every path under them takes the form the entrypoint selected rather than the first alone. For single-file changes, the path on its own line is enough.
|
|
44
48
|
|
|
45
49
|
## Key paths
|
|
46
50
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineConfig, devices } from '@playwright/test'
|
|
2
2
|
|
|
3
3
|
const isCI = !!process.env.CI
|
|
4
|
+
const baseURL = `http://localhost:${5173 + (Number(process.env.WORKTREE_PORT_OFFSET) || 0)}`
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
7
|
testDir: 'e2e',
|
|
@@ -10,7 +11,7 @@ export default defineConfig({
|
|
|
10
11
|
reporter: isCI ? 'list' : 'html',
|
|
11
12
|
use: {
|
|
12
13
|
trace: 'on-first-retry',
|
|
13
|
-
baseURL
|
|
14
|
+
baseURL,
|
|
14
15
|
},
|
|
15
16
|
projects: [
|
|
16
17
|
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
@@ -19,7 +20,7 @@ export default defineConfig({
|
|
|
19
20
|
],
|
|
20
21
|
webServer: {
|
|
21
22
|
command: 'bun run dev',
|
|
22
|
-
url:
|
|
23
|
-
reuseExistingServer:
|
|
23
|
+
url: baseURL,
|
|
24
|
+
reuseExistingServer: false,
|
|
24
25
|
},
|
|
25
26
|
})
|
|
@@ -3,6 +3,8 @@ import react from '@vitejs/plugin-react'
|
|
|
3
3
|
import path from 'path'
|
|
4
4
|
import { defineConfig } from 'vite'
|
|
5
5
|
|
|
6
|
+
const portOffset = Number(process.env.WORKTREE_PORT_OFFSET) || 0
|
|
7
|
+
|
|
6
8
|
export default defineConfig({
|
|
7
9
|
plugins: [react(), tailwindcss()],
|
|
8
10
|
resolve: {
|
|
@@ -11,4 +13,12 @@ export default defineConfig({
|
|
|
11
13
|
},
|
|
12
14
|
},
|
|
13
15
|
base: process.env.VITE_BASE_URL ?? '/',
|
|
16
|
+
server: {
|
|
17
|
+
port: 5173 + portOffset,
|
|
18
|
+
strictPort: true,
|
|
19
|
+
},
|
|
20
|
+
preview: {
|
|
21
|
+
port: 4173 + portOffset,
|
|
22
|
+
strictPort: true,
|
|
23
|
+
},
|
|
14
24
|
})
|
|
@@ -11,11 +11,13 @@ packages = [
|
|
|
11
11
|
]
|
|
12
12
|
|
|
13
13
|
[scripts]
|
|
14
|
-
"dev" = "vite"
|
|
14
|
+
"dev" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) vite"
|
|
15
15
|
"build" = "tsc --noEmit && vite build"
|
|
16
|
-
"preview" = "vite preview"
|
|
16
|
+
"preview" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) vite preview"
|
|
17
17
|
"typecheck" = "tsc --noEmit"
|
|
18
18
|
"setup" = "./scripts/setup.sh"
|
|
19
19
|
|
|
20
20
|
[scripts.override]
|
|
21
21
|
"build" = "tsc --noEmit && vite build"
|
|
22
|
+
"dev" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) vite"
|
|
23
|
+
"preview" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) vite preview"
|
|
@@ -16,16 +16,16 @@ The vite-react stack covers Vite + React + TypeScript projects: web apps and Chr
|
|
|
16
16
|
|
|
17
17
|
## What ships as golden configs
|
|
18
18
|
|
|
19
|
-
- `vite.config.ts`: `@vitejs/plugin-react`, `@tailwindcss/vite`, `@` path alias to `./src`, `VITE_BASE_URL` env for base path.
|
|
19
|
+
- `vite.config.ts`: `@vitejs/plugin-react`, `@tailwindcss/vite`, `@` path alias to `./src`, `VITE_BASE_URL` env for base path. Dev port `5173` and preview port `4173`, each plus `WORKTREE_PORT_OFFSET`, both with `strictPort`.
|
|
20
20
|
- `vitest.config.ts`: merges from `vite.config.ts`, jsdom, globals, setup file, `passWithNoTests: true`, v8 coverage.
|
|
21
|
-
- `playwright.config.ts`: all browsers, `webServer` on `bun run dev` at port 5173
|
|
21
|
+
- `playwright.config.ts`: all browsers, `webServer` on `bun run dev` at port `5173` plus `WORKTREE_PORT_OFFSET`, `reuseExistingServer: false`, trace under `use`.
|
|
22
22
|
- `tsconfig.json`: unified, `noEmit: true`, `skipLibCheck: true`, `@/` paths, `vitest/globals` and `@testing-library/jest-dom` in types.
|
|
23
23
|
|
|
24
24
|
## Chrome extension variant
|
|
25
25
|
|
|
26
26
|
When scaffolding a Chrome extension, override the installed golden configs:
|
|
27
27
|
|
|
28
|
-
- `vite.config.ts`: use `crx({ manifest })` and `zip()` from `@crxjs/vite-plugin` instead of `react()` alone.
|
|
28
|
+
- `vite.config.ts`: use `crx({ manifest })` and `zip()` from `@crxjs/vite-plugin` instead of `react()` alone. Keep the derived `server.port` and `server.strictPort: true`, set `server.hmr.clientPort` to the same derived value, and add `chrome-extension://` to CORS origins. Drop `VITE_BASE_URL`.
|
|
29
29
|
- `vitest.config.ts`: use a standalone `defineConfig` (no `mergeConfig`). crxjs plugin breaks Vitest. Declare `@vitejs/plugin-react` and `@tailwindcss/vite` directly. Add `**/release/**` to excludes and `manifest.config.ts`, `**/*.d.ts` to coverage excludes.
|
|
30
30
|
- `playwright.config.ts`: chromium-only (Firefox and WebKit cannot run extensions). Bundled `chromium` channel. No `baseURL` or `webServer`. Tests load the built extension directly from `dist/`.
|
|
31
31
|
- `e2e/fixtures.ts`: extend Playwright base `test` with `context` (persistent context loading the extension from `dist/`) and `extensionId` (extracted from service worker URL). Rename `use` to `apply` to avoid the React hooks ESLint rule. `waitForEvent('serviceworker')` blocks until the MV3 service worker registers.
|
|
@@ -45,7 +45,7 @@ When scaffolding a Chrome extension, override the installed golden configs:
|
|
|
45
45
|
|
|
46
46
|
Append to the `## Scripts` table:
|
|
47
47
|
|
|
48
|
-
| `bun run dev` | Start the Vite dev server on port 5173. |
|
|
48
|
+
| `bun run dev` | Start the Vite dev server on port 5173, plus this worktree's port offset. |
|
|
49
49
|
| `bun run build` | Typecheck then build the production bundle. |
|
|
50
50
|
| `bun run preview` | Serve the built bundle locally. |
|
|
51
51
|
| `bun run typecheck` | Run `tsc --noEmit`. |
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Prints a port for this working directory: the base itself in a normal
|
|
5
|
+
# checkout, and the base plus a per-worktree offset in a linked git worktree,
|
|
6
|
+
# so two worktrees of one repository never serve on one port.
|
|
7
|
+
|
|
8
|
+
base="${1:-0}"
|
|
9
|
+
band=50
|
|
10
|
+
|
|
11
|
+
offset() {
|
|
12
|
+
if [[ -n "${WORKTREE_PORT_OFFSET:-}" ]]; then
|
|
13
|
+
echo "$WORKTREE_PORT_OFFSET"
|
|
14
|
+
return
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
local git_dir common_dir name
|
|
18
|
+
git_dir=$(git rev-parse --git-dir 2>/dev/null) || {
|
|
19
|
+
echo 0
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || {
|
|
23
|
+
echo 0
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if [[ "$(cd "$git_dir" && pwd -P)" == "$(cd "$common_dir" && pwd -P)" ]]; then
|
|
28
|
+
echo 0
|
|
29
|
+
return
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
name=$(basename "$(git rev-parse --show-toplevel)")
|
|
33
|
+
echo $(($(printf '%s' "$name" | cksum | cut -d' ' -f1) % band + 1))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
echo $((base + $(offset)))
|
|
@@ -42,13 +42,13 @@ packages = [
|
|
|
42
42
|
"test:run" = "vitest run --reporter=verbose"
|
|
43
43
|
"test:ui" = "vitest --ui"
|
|
44
44
|
"test:coverage" = "vitest run --coverage"
|
|
45
|
-
"test:e2e" = "playwright test"
|
|
46
|
-
"test:e2e:ui" = "playwright test --ui"
|
|
45
|
+
"test:e2e" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) playwright test"
|
|
46
|
+
"test:e2e:ui" = "WORKTREE_PORT_OFFSET=$(bash scripts/worktree-port.sh) playwright test --ui"
|
|
47
47
|
"test:e2e:report" = "playwright show-report"
|
|
48
48
|
"check:full" = "./scripts/verify.sh && bun run test:e2e"
|
|
49
49
|
|
|
50
50
|
[scripts.override]
|
|
51
|
-
"screenshot" = "bash scripts/screenshot.sh"
|
|
51
|
+
"screenshot" = "PREVIEW_PORT=$(bash scripts/worktree-port.sh 4173) bash scripts/screenshot.sh"
|
|
52
52
|
|
|
53
53
|
[gitignore]
|
|
54
54
|
"# Build" = ["dist/"]
|
package/tooling/web/reference.md
CHANGED
|
@@ -16,6 +16,7 @@ Golden config files live in `tooling/web/configs/` and are copied into the targe
|
|
|
16
16
|
- `.vscode/extensions.json` and `.vscode/settings.json`: editor wiring for ESLint, Tailwind, Playwright, Vitest.
|
|
17
17
|
- `.github/workflows/verify.yml`: `static-checks`, `unit-tests`, `build-verify`, and `e2e-tests` jobs.
|
|
18
18
|
- `scripts/verify.sh`: extends base verify with typecheck, lint, unit tests, and build in the full order.
|
|
19
|
+
- `scripts/worktree-port.sh`: prints a base port plus this working directory's offset. Called with no argument it prints the offset alone.
|
|
19
20
|
|
|
20
21
|
## What stays in per-stack adapters
|
|
21
22
|
|
|
@@ -32,6 +33,17 @@ Framework glue lives in `tooling/vite-react/configs/` or `tooling/astro/configs/
|
|
|
32
33
|
- Path alias `@` maps to `./src` in both tsconfig and the framework's build config.
|
|
33
34
|
- Tsconfig is unified at root with `noEmit: true` in Vite stacks. Astro uses the scaffold default from `@astrojs/check`.
|
|
34
35
|
|
|
36
|
+
## Ports
|
|
37
|
+
|
|
38
|
+
Two worktrees of one repository run the same stack, so a fixed port makes the second one attach to the first.
|
|
39
|
+
|
|
40
|
+
- Derive every served port from `scripts/worktree-port.sh`. Never write a port literal into a script string.
|
|
41
|
+
- Read `WORKTREE_PORT_OFFSET` in a config and add it to the stack's default port. Unset yields the default, so a plain clone keeps the port it has always served on.
|
|
42
|
+
- Draw the offset from a band of 50, hashed from the worktree folder name. Two worktrees can hash to one offset, so set `WORKTREE_PORT_OFFSET` by hand to break a tie.
|
|
43
|
+
- Force-replace `dev` and `preview` through `[scripts.override]`. Both stacks' scaffolds define those keys, and a plain `[scripts]` entry never replaces a key the scaffold already wrote.
|
|
44
|
+
- Set `strictPort` on every dev and preview server. A server that walks to the next free port serves where nothing is looking for it.
|
|
45
|
+
- Set Playwright `reuseExistingServer: false`. Reuse attaches to whatever answers on the port, which reports a pass against another branch's code and prints nothing to say so.
|
|
46
|
+
|
|
35
47
|
## Anti-patterns
|
|
36
48
|
|
|
37
49
|
Sticky negative knowledge. Do not relearn.
|