@ttt-productions/dev-standards 0.0.3 → 0.0.5
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.base.md +30 -34
- package/development-preferences.md +70 -11
- package/dist/node/eslint.d.ts.map +1 -1
- package/dist/node/eslint.js +15 -7
- package/dist/node/eslint.js.map +1 -1
- package/model-tiers.md +1 -1
- package/package.json +1 -1
- package/playbooks/codebase-hygiene-playbook.md +15 -1
- package/playbooks/crosswalk.md +3 -3
- package/playbooks/ux-audit-playbook.md +1 -1
- package/rules/quality/core-rules.md +4 -4
- package/workflows/claude/fan-out-implementation.md +12 -8
- package/workflows/claude/fix-known-errors-inline.md +3 -1
- package/workflows/claude/implement-an-approved-plan.md +3 -2
- package/workflows/claude/mechanical-fan-out.md +2 -2
- package/workflows/claude/work-a-project-step.md +3 -2
package/claude/CLAUDE.base.md
CHANGED
|
@@ -144,10 +144,12 @@ Before starting a project's work:
|
|
|
144
144
|
Every release of an `@ttt-productions/*` package hands its adoption to each app through a file in
|
|
145
145
|
`<PACKAGES_REPO>/docs/work/adoptions/<app>/`.
|
|
146
146
|
|
|
147
|
-
- **The package session writes them
|
|
148
|
-
command, it writes one adoption file per released
|
|
149
|
-
package
|
|
150
|
-
|
|
147
|
+
- **The package session writes them only for the apps that aren't in the conversation.** In the same
|
|
148
|
+
change as the package edit, before the release command, it writes one adoption file per released
|
|
149
|
+
package for each app outside the conversation — an app-data package's file goes only to its own
|
|
150
|
+
app. A package session cannot see which packages another product installs, so every other released
|
|
151
|
+
package gets a file for each of those apps. The app the conversation is working in adopts the
|
|
152
|
+
release itself, right after the install.
|
|
151
153
|
- **The version it names** is the version the release will publish: one patch above the package's
|
|
152
154
|
`package.json` version before the release.
|
|
153
155
|
- **The file** is one item in the `docs/work/` item format (`<DEV_STANDARDS>/work/README.md`) —
|
|
@@ -198,9 +200,8 @@ step 5.
|
|
|
198
200
|
## The Normal `<APP_NAME>` Flows (named workflows — "do the normal dev/test flow" resolves HERE)
|
|
199
201
|
|
|
200
202
|
These two named flows are the standing default for how work in these repos is verified and
|
|
201
|
-
sequenced. They are DEFAULTS, not cages: DJ may override any step with explicit
|
|
202
|
-
|
|
203
|
-
using the machine"), and an override governs exactly what it names, nothing more. A repo changes a
|
|
203
|
+
sequenced. They are DEFAULTS, not cages: DJ may override any step with an explicit instruction; the
|
|
204
|
+
override governs exactly what it names, for that conversation only. A repo changes a
|
|
204
205
|
flow only under its `<APP_NAME>` additions heading. In a repo whose `<APP_NAME>` is none
|
|
205
206
|
(ttt-packages) the Test Flow does not apply — its gate is its own `CLAUDE.md` § Verification — and
|
|
206
207
|
the Dev Flow's steps 1–4 are its whole share.
|
|
@@ -221,9 +222,10 @@ the Dev Flow's steps 1–4 are its whole share.
|
|
|
221
222
|
(RULE-STD-QUALITY-008 — Claude operates the restart).
|
|
222
223
|
5. Kill `dev:e2e` when done and STOP — wait for DJ's push.
|
|
223
224
|
|
|
224
|
-
**
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
**In a conversation DJ has called an orchestrator, the Normal Test Flow is run by one test agent,
|
|
226
|
+
never the orchestrator.** A failure goes back to the orchestrator, which sends it to the agent that
|
|
227
|
+
built the failing code; that agent fixes it and reports. The same test agent then reruns the flow,
|
|
228
|
+
restarting `dev:e2e` per stage 4. In an inline conversation, the session runs the Test Flow itself.
|
|
227
229
|
|
|
228
230
|
Not part of this flow: hosted-dev E2E runs (always separately commanded — `<HOSTED_E2E_DOC>`) and
|
|
229
231
|
`npm run dev:local` (DJ's manual dev surface).
|
|
@@ -235,29 +237,23 @@ Not part of this flow: hosted-dev E2E runs (always separately commanded — `<HO
|
|
|
235
237
|
change as the package edit.
|
|
236
238
|
3. ttt-packages `npm run test:quiet` green.
|
|
237
239
|
4. Hand DJ ONLY the release command (`./scripts/release-multiple.sh <folders…> patch`, its own fenced
|
|
238
|
-
block)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
`package.json` version in the checkout at the moment DJ speaks, which can still be the old one
|
|
257
|
-
(compared against npm `@latest`, it reads a stale match as "live"). A version is available only
|
|
258
|
-
when npm `@latest` names it AND its tarball
|
|
259
|
-
(`https://registry.npmjs.org/@ttt-productions/<pkg>/-/<pkg>-<version>.tgz`) answers 200: the
|
|
260
|
-
registry can report the new `@latest` minutes before it serves the tarball, and an install in that
|
|
261
|
-
gap fails `E404`.
|
|
240
|
+
block), with one line under it naming the versions it will publish — the same versions its
|
|
241
|
+
adoption files name, one patch above each package's `package.json` version before the release
|
|
242
|
+
(e.g. "Publishes chat-core 0.11.8 and chat-react 0.13.20.") — then STOP. No install commands:
|
|
243
|
+
Claude does the installs.
|
|
244
|
+
5. DJ runs the publish and says so, at any point: the script just started, its preflight still
|
|
245
|
+
running, or the packages already published and GitHub Actions running. Claude then starts a
|
|
246
|
+
20-minute clock and checks npm every 30 seconds for exactly the versions named in step 4 — it never
|
|
247
|
+
re-reads `package.json` or git to find them. A version is available only when npm `@latest` names
|
|
248
|
+
it AND its tarball (`https://registry.npmjs.org/@ttt-productions/<pkg>/-/<pkg>-<version>.tgz`)
|
|
249
|
+
answers 200: the registry can report the new `@latest` minutes before it serves the tarball, and
|
|
250
|
+
an install in that gap fails `E404`. Then Claude runs the installs itself (`@latest
|
|
251
|
+
--prefer-online`, at the root and in every consuming folder that declares the package —
|
|
252
|
+
`<INSTALL_FOLDERS>`), confirms each lockfile names the new version, adopts the release in its own
|
|
253
|
+
app (§ Adoption files), and continues straight into the app work with no further go. While it
|
|
254
|
+
polls, the session works on whatever does not depend on the release — the publish-wait overlap in
|
|
255
|
+
`<DEV_STANDARDS>/development-preferences.md` § Shared-package publish. A version still missing from
|
|
256
|
+
npm after 20 minutes = stop and tell DJ which package; never install anything but the published
|
|
257
|
+
version.
|
|
262
258
|
6. All `<APP_NAME>` work; when the entire change set is done, run the Normal `<APP_NAME>` Test Flow.
|
|
263
259
|
7. Stop and wait for DJ's push.
|
|
@@ -23,6 +23,16 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
23
23
|
it goes. Nothing is filed, parked, or written up without DJ's word.
|
|
24
24
|
- A doc's "staged / later / deferred" label is never DJ's decision; only DJ's own explicit deferral
|
|
25
25
|
parks an item.
|
|
26
|
+
- **DJ's rules live only in his rule docs** — the rulebooks, this file, `CLAUDE.md` / `AGENTS.md`,
|
|
27
|
+
and the workflows. An instruction DJ gives in a conversation lasts for that conversation only; it
|
|
28
|
+
becomes a standing rule only when he says to write it into the rules. No plan, work item, ledger,
|
|
29
|
+
or prompt ever holds a rule, an instruction, or reasoning, and a session never takes a rule from
|
|
30
|
+
one.
|
|
31
|
+
- **There are no handoff docs.** When DJ asks to get ready for a new conversation, the session gives
|
|
32
|
+
a small copy-paste prompt in chat that points at the items being worked; the items hold only what
|
|
33
|
+
is left. The prompt carries nothing else except open questions DJ chose to move. An item whose
|
|
34
|
+
only remaining work is the Normal Test Flow or a hosted E2E run is deleted; if a conversation
|
|
35
|
+
switches at that point, the prompt says to run it.
|
|
26
36
|
- "Finish everything" / "do it all" means every single thing DJ has decided, with nothing left
|
|
27
37
|
undone or parked. It never means deciding anything for DJ: when finishing needs a decision he has
|
|
28
38
|
not made, ask that one question and keep doing the rest. Never stop for complexity, size, time of
|
|
@@ -51,22 +61,40 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
51
61
|
- An answer that REDIRECTS is not approval to build the revision — show the revised artifact in
|
|
52
62
|
chat and get a clean confirm before implementing.
|
|
53
63
|
- No new-conversation or handoff proposals unless DJ asks.
|
|
64
|
+
- **Every question is tracked until DJ answers it.** A question about the item being worked is asked
|
|
65
|
+
when it is found; the parts of that item that depend on the answer wait, and everything else keeps
|
|
66
|
+
going. A problem found outside the item never stops work: it is reported in chat with what was
|
|
67
|
+
verified and a recommendation, and stays open for DJ. When DJ has said he is away, no question
|
|
68
|
+
blocks: it goes in chat, the session finishes every part that does not depend on it, and moves to
|
|
69
|
+
the next item.
|
|
70
|
+
- **No full gate while any question is open** — the Normal Test Flow, or a repo's own `test:quiet`
|
|
71
|
+
gate, ttt-packages' included. An answer can change code, and a gate run before it is worthless.
|
|
72
|
+
The open questions are asked first, any code they change is done, and then the gate runs once.
|
|
73
|
+
- Open questions are asked one at a time, as questions, never buried in a status message. The
|
|
74
|
+
session never answers, files, or drops one itself, and never labels one "FYI" or "not blocking" —
|
|
75
|
+
whether it matters is DJ's call.
|
|
76
|
+
- When DJ asks whether the conversation is good to end, or says to start a new one, the session first
|
|
77
|
+
says how many questions are open and asks whether to answer them here or move them into the new
|
|
78
|
+
conversation's prompt.
|
|
54
79
|
|
|
55
80
|
## Git, publish, deploy, and the dev stacks
|
|
56
81
|
|
|
57
|
-
- DJ does ALL git
|
|
58
|
-
deployments.
|
|
59
|
-
|
|
60
|
-
change as a sign of someone else's work,
|
|
61
|
-
with DJ.
|
|
82
|
+
- DJ does ALL git — staging, commits, pushes, branches, publishing, installs he has not handed over,
|
|
83
|
+
and deployments. An AI runs none of it unless DJ commands that exact action in the conversation;
|
|
84
|
+
otherwise it hands him the exact command. DJ stages files continuously while work is running so he
|
|
85
|
+
can see what is changing; never treat a staged/unstaged change as a sign of someone else's work,
|
|
86
|
+
and never bring up staging, the index, or other git state with DJ.
|
|
87
|
+
- Git and GitHub (`git log`, `git status`, diffs, `gh`, Actions runs) are read only when DJ says it is
|
|
88
|
+
needed. A problem DJ brings is fixed from the current code; when and why it changed is not looked
|
|
89
|
+
up unless he asks.
|
|
62
90
|
- Never instruct DJ to push or deploy — deploy IS `git push`. Never nag about deploys, indexes, or
|
|
63
91
|
seed buttons.
|
|
64
92
|
- Never discard, overwrite, reset, clean, or revert existing work unless DJ explicitly asks.
|
|
65
93
|
Preserve unrelated uncommitted work; DJ or another tool may be working concurrently.
|
|
66
94
|
- `dev:local` (DJ's manual dev stack) is DJ-only, always — never start or stop it.
|
|
67
95
|
- `dev:e2e` (the verification stack) is session-operated only inside the repo's Normal Test Flow or
|
|
68
|
-
on DJ's explicit command. DJ's in-conversation instruction overrides any flow step
|
|
69
|
-
|
|
96
|
+
on DJ's explicit command. DJ's in-conversation instruction overrides any flow step and governs
|
|
97
|
+
exactly what it names, for that conversation only.
|
|
70
98
|
- Never kill a running dev stack on your own judgment ("ports look wrong" = say what you see and
|
|
71
99
|
WAIT). Never run e2e against a stale build.
|
|
72
100
|
- Hosted-dev E2E runs happen only when DJ commands one, and follow the repo's hosted-E2E run doc
|
|
@@ -118,6 +146,13 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
118
146
|
- Verify at the cost of the fix: a one-file fix runs its single file first; the broad gate only
|
|
119
147
|
when the blast radius warrants it. Never run multiple full repository gates concurrently.
|
|
120
148
|
- A flaky test is a real bug — root-cause it; more than one flake is structural.
|
|
149
|
+
- **The quickest, easiest fix is never the goal; the right one is.** A fix is ready to propose only
|
|
150
|
+
once the session has worked out the real problem rather than its symptom; whether it is a class
|
|
151
|
+
of problem and where else it lives; whether an owner already solves it and whether the fix sits
|
|
152
|
+
in the right home; whether the fix is a hack around a larger design change; what else it changes
|
|
153
|
+
— data, the other product, docs, tests, guards; what would let the class back in; and whether the
|
|
154
|
+
problem as stated is even true in the code. A fix that contradicts a rule or a design is a
|
|
155
|
+
question for DJ.
|
|
121
156
|
|
|
122
157
|
## Engineering judgment
|
|
123
158
|
|
|
@@ -130,15 +165,26 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
130
165
|
- No half-working, dead, or known-bad code — build it or remove it. On a second sighting of a defect
|
|
131
166
|
class, tell DJ and propose killing the CLASS system-wide plus a regression guard; do it only on
|
|
132
167
|
DJ's command.
|
|
168
|
+
- **Never make yourself an exception.** Code that breaks a rule, guard, lint check, or type check is
|
|
169
|
+
fixed. An exception is asked for only when there is genuinely no other way, and it goes to DJ with
|
|
170
|
+
that reason, why no fix works. Nothing is exempted without his yes. Every exception he approves carries
|
|
171
|
+
its reason at the exception itself, so anyone reading it years later knows at once why it exists:
|
|
172
|
+
the guard entry's reason field, the comment on the `eslint-disable` / `prettier-ignore` /
|
|
173
|
+
`@ts-expect-error` / `@ts-ignore` line, or, for a design-level exception, the design doc it
|
|
174
|
+
belongs to. The reason states why the exception exists, never who approved it. Keeping an
|
|
175
|
+
exception an agent added is the same decision, and goes to DJ the same way.
|
|
133
176
|
- Reviewing an external plan (ChatGPT/Codex/etc.) means answering "is this the BEST code to solve
|
|
134
177
|
my problem" — never just verifying its claims. Evaluate the design space (alternatives, root cause,
|
|
135
178
|
structure) and fold an optimality verdict into the reviewed doc.
|
|
136
179
|
- V1-scope discipline: confirm the trigger exists in V1 before asking DJ to design.
|
|
137
180
|
- Docs stay lean — never a "done" log. Tracking docs hold only remaining work; finished work is
|
|
138
181
|
DELETED.
|
|
182
|
+
- **Working files** a session needs — extracted text, a one-off script, intermediate output — go in
|
|
183
|
+
`C:\DjDev\claude-scratchpad\<YYYY-MM-DD>-<topic>\`, never in a temp folder, loose in `C:\DjDev`,
|
|
184
|
+
or inside a repo. A session writes there only when the work needs a file, never for a conversation
|
|
185
|
+
that is only talking, and deletes its folder when that work is done.
|
|
139
186
|
- Working state that must outlive the session — orchestration ledgers, agent change logs, file
|
|
140
|
-
claims
|
|
141
|
-
the work it tracks and is deleted when the work finishes.
|
|
187
|
+
claims — lives in the repo beside the work it tracks and is deleted when the work finishes.
|
|
142
188
|
- Help with EVERY step, including non-code and operational ones — real artifacts (policies,
|
|
143
189
|
runbooks, click-by-click), never a "your job" checklist. Walk-throughs go one step at a time, dev
|
|
144
190
|
AND prod. A non-code or console step becomes a self-contained Claude web prompt (full context,
|
|
@@ -150,11 +196,24 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
150
196
|
|
|
151
197
|
## Agents
|
|
152
198
|
|
|
199
|
+
- **Every conversation is inline unless DJ says, in that conversation, that it is an orchestrator.**
|
|
200
|
+
No doc, plan, ledger, or prompt makes a conversation an orchestrator.
|
|
201
|
+
- **Inline:** the session does the research and the work itself. When a piece of the work would be
|
|
202
|
+
done better by agents — LOW for mechanical work applied from written examples, HIGH for thinking
|
|
203
|
+
work or review — it proposes them to DJ — how many, each one's tier, its exact task, and its
|
|
204
|
+
files — and launches them only on his yes. Launching agents, a predefined wave included, never makes it an orchestrator; it
|
|
205
|
+
still runs its own Normal Test Flow.
|
|
206
|
+
- **Orchestrator:** the session saves its context for managing agents and does not read or edit code
|
|
207
|
+
itself. It first launches read-only agents to understand the system, then gives each building
|
|
208
|
+
agent the high-level picture and the design: build this, this way. An agent that finds a better or
|
|
209
|
+
different way stops and comes back instead of building it. Agents run in waves of a few at a time,
|
|
210
|
+
each owning its files, no file owned twice. LOW agents never decide; they apply the examples the
|
|
211
|
+
HIGH session gives them.
|
|
153
212
|
- The main session owns edits unless DJ explicitly approves parallel implementers with disjoint file
|
|
154
213
|
ownership. Nothing spawns without DJ's yes.
|
|
155
214
|
- Every prompt that spawns an agent names that agent's mandatory reading as explicit
|
|
156
|
-
read-before-acting paths (RULE-STD-ENG-010), hard-bans spawning further agents
|
|
157
|
-
clause: anything unexpected, or a needed package change, stops the agent and comes back to the
|
|
215
|
+
read-before-acting paths (RULE-STD-ENG-010), hard-bans spawning further agents and running any
|
|
216
|
+
git or `gh` command, and carries a stop clause: anything unexpected, or a needed package change, stops the agent and comes back to the
|
|
158
217
|
session.
|
|
159
218
|
- How a given kind of conversation runs its agents is its development workflow's procedure
|
|
160
219
|
(the shared `<DEV_STANDARDS>/workflows/`, plus any app-only one in `<APP_WORKFLOWS>`); this file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../../src/node/eslint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;
|
|
1
|
+
{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../../src/node/eslint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIrC,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,SAAS,MAAM,CAAC,MAAM,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,sBAAuB,SAAQ,mBAAmB;IACjE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;CACzB;AAiGD,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAmB1B;AAED,wBAAsB,2BAA2B,CAC/C,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAoC1B"}
|
package/dist/node/eslint.js
CHANGED
|
@@ -15,12 +15,7 @@ const APP_IGNORES = [
|
|
|
15
15
|
'out/**',
|
|
16
16
|
'dist/**',
|
|
17
17
|
'build/**',
|
|
18
|
-
'*.config.js',
|
|
19
|
-
'*.config.ts',
|
|
20
18
|
'next-env.d.ts',
|
|
21
|
-
'instrumentation.ts',
|
|
22
|
-
'instrumentation-client.ts',
|
|
23
|
-
'sentry.*.config.ts',
|
|
24
19
|
'.next/types/**',
|
|
25
20
|
'.next/dev/**',
|
|
26
21
|
'docs/**',
|
|
@@ -40,6 +35,7 @@ const APP_RULES = {
|
|
|
40
35
|
},
|
|
41
36
|
},
|
|
42
37
|
rules: {
|
|
38
|
+
// RULE-STD-MEDIA-101 bans next/image, the replacement this rule demands; served files render through the media renderer.
|
|
43
39
|
'@next/next/no-img-element': 'off',
|
|
44
40
|
'@typescript-eslint/no-unused-vars': [
|
|
45
41
|
'error',
|
|
@@ -49,7 +45,6 @@ const APP_RULES = {
|
|
|
49
45
|
destructuredArrayIgnorePattern: '^_',
|
|
50
46
|
},
|
|
51
47
|
],
|
|
52
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
53
48
|
'react-hooks/rules-of-hooks': 'error',
|
|
54
49
|
'react-hooks/exhaustive-deps': 'error',
|
|
55
50
|
'react-hooks/set-state-in-effect': 'error',
|
|
@@ -59,6 +54,18 @@ const APP_RULES = {
|
|
|
59
54
|
'react-hooks/purity': 'error',
|
|
60
55
|
},
|
|
61
56
|
};
|
|
57
|
+
// Type-aware: each TypeScript file is read through the nearest tsconfig.json that includes it.
|
|
58
|
+
const typeAwareNoAny = (files) => ({
|
|
59
|
+
files,
|
|
60
|
+
languageOptions: { parserOptions: { projectService: true } },
|
|
61
|
+
rules: {
|
|
62
|
+
'@typescript-eslint/no-unsafe-argument': 'error',
|
|
63
|
+
'@typescript-eslint/no-unsafe-assignment': 'error',
|
|
64
|
+
'@typescript-eslint/no-unsafe-call': 'error',
|
|
65
|
+
'@typescript-eslint/no-unsafe-member-access': 'error',
|
|
66
|
+
'@typescript-eslint/no-unsafe-return': 'error',
|
|
67
|
+
},
|
|
68
|
+
});
|
|
62
69
|
// eslint-config-next registers jsx-a11y and flat config errors on a second registration, so this
|
|
63
70
|
// sets rules only; a severity-only entry keeps the options Next set (its Image alt-text options).
|
|
64
71
|
function jsxA11yRecommended(nextConfigs, severity) {
|
|
@@ -93,6 +100,7 @@ export async function createAppEslintConfig(options = {}) {
|
|
|
93
100
|
...nextTypescript,
|
|
94
101
|
jsxA11yRecommended(nextCoreWebVitals, options.a11y ?? 'error'),
|
|
95
102
|
APP_RULES,
|
|
103
|
+
typeAwareNoAny(['**/*.ts', '**/*.tsx']),
|
|
96
104
|
...(options.configs ?? []),
|
|
97
105
|
prettierCompat,
|
|
98
106
|
];
|
|
@@ -111,7 +119,6 @@ export async function createFunctionsEslintConfig(options = {}) {
|
|
|
111
119
|
languageOptions: { globals: nodeGlobals },
|
|
112
120
|
rules: {
|
|
113
121
|
'@typescript-eslint/no-unused-expressions': ['error', { allowShortCircuit: true }],
|
|
114
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
115
122
|
'@typescript-eslint/no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],
|
|
116
123
|
'no-undef': 'off',
|
|
117
124
|
},
|
|
@@ -130,6 +137,7 @@ export async function createFunctionsEslintConfig(options = {}) {
|
|
|
130
137
|
'no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],
|
|
131
138
|
},
|
|
132
139
|
},
|
|
140
|
+
typeAwareNoAny(['**/*.ts']),
|
|
133
141
|
...(options.configs ?? []),
|
|
134
142
|
prettierCompat,
|
|
135
143
|
];
|
package/dist/node/eslint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/node/eslint.ts"],"names":[],"mappings":"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","sourcesContent":["import js from '@eslint/js';\nimport type { Linter } from 'eslint';\nimport prettierCompat from 'eslint-config-prettier';\nimport tseslint from 'typescript-eslint';\n\nexport interface EslintPresetOptions {\n ignores?: readonly string[];\n configs?: readonly Linter.Config[];\n}\n\nexport interface AppEslintPresetOptions extends EslintPresetOptions {\n a11y?: 'error' | 'warn';\n}\n\nconst ZERO_SUPPRESSION: Linter.Config = { linterOptions: { noInlineConfig: true } };\n\nconst APP_IGNORES = [\n '.next/**',\n 'node_modules/**',\n '.firebase/**',\n 'functions/**',\n // ESLint does not honor .gitignore, so the E2E harness's generated artifacts are ignored here.\n 'e2e-hosted-dev/.browser-profiles/**',\n 'e2e-hosted-dev/test-results/**',\n 'e2e-hosted-dev/failed-run-archive/**',\n 'test-results/**',\n 'out/**',\n 'dist/**',\n 'build/**',\n '*.config.js',\n '*.config.ts',\n 'next-env.d.ts',\n 'instrumentation.ts',\n 'instrumentation-client.ts',\n 'sentry.*.config.ts',\n '.next/types/**',\n '.next/dev/**',\n 'docs/**',\n];\n\nconst UNUSED_VARS_ALLOW_UNDERSCORE = {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n};\n\nconst APP_RULES: Linter.Config = {\n files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'],\n // eslint-config-next registers react-hooks and flat config errors on a redefinition: rules only.\n languageOptions: {\n parserOptions: {\n ecmaVersion: 'latest',\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n },\n },\n rules: {\n '@next/next/no-img-element': 'off',\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n ...UNUSED_VARS_ALLOW_UNDERSCORE,\n caughtErrorsIgnorePattern: '^_',\n destructuredArrayIgnorePattern: '^_',\n },\n ],\n '@typescript-eslint/no-explicit-any': 'off',\n 'react-hooks/rules-of-hooks': 'error',\n 'react-hooks/exhaustive-deps': 'error',\n 'react-hooks/set-state-in-effect': 'error',\n 'react-hooks/refs': 'error',\n 'react-hooks/immutability': 'error',\n 'react-hooks/incompatible-library': 'error',\n 'react-hooks/purity': 'error',\n },\n};\n\ntype RuleEntry = NonNullable<Linter.Config['rules']>[string];\n\n// eslint-config-next registers jsx-a11y and flat config errors on a second registration, so this\n// sets rules only; a severity-only entry keeps the options Next set (its Image alt-text options).\nfunction jsxA11yRecommended(\n nextConfigs: Linter.Config[],\n severity: 'error' | 'warn',\n): Linter.Config {\n const plugin = nextConfigs.find((config) => config.plugins?.['jsx-a11y'])?.plugins?.['jsx-a11y'];\n const recommended = (plugin?.configs?.recommended as Linter.Config | undefined)?.rules;\n if (!recommended) {\n throw new Error(\n 'eslint-config-next no longer registers eslint-plugin-jsx-a11y with a recommended config',\n );\n }\n const rules: Record<string, RuleEntry> = {};\n for (const [name, entry] of Object.entries(recommended)) {\n if (entry === undefined) continue;\n const level = Array.isArray(entry) ? entry[0] : entry;\n if (level === 'off' || level === 0) continue;\n rules[name] = Array.isArray(entry) ? [severity, ...entry.slice(1)] : severity;\n }\n return { files: ['**/*.tsx', '**/*.jsx'], rules };\n}\n\nexport async function createAppEslintConfig(\n options: AppEslintPresetOptions = {},\n): Promise<Linter.Config[]> {\n // Imported here: the Functions tree loads this module without eslint-config-next installed.\n const [{ default: nextCoreWebVitals }, { default: nextTypescript }] = await Promise.all([\n import('eslint-config-next/core-web-vitals'),\n import('eslint-config-next/typescript'),\n ]);\n return [\n { ignores: [...APP_IGNORES, ...(options.ignores ?? [])] },\n ZERO_SUPPRESSION,\n js.configs.recommended,\n ...(tseslint.configs.recommended as Linter.Config[]),\n ...(nextCoreWebVitals as Linter.Config[]),\n ...(nextTypescript as Linter.Config[]),\n jsxA11yRecommended(nextCoreWebVitals as Linter.Config[], options.a11y ?? 'error'),\n APP_RULES,\n ...(options.configs ?? []),\n prettierCompat,\n ];\n}\n\nexport async function createFunctionsEslintConfig(\n options: EslintPresetOptions = {},\n): Promise<Linter.Config[]> {\n // Imported here: the Next.js tree loads this module without globals installed.\n const { default: globals } = await import('globals');\n const nodeGlobals = { ...globals.node, ...globals.es2021 };\n return [\n { ignores: ['lib/**', 'build/**', ...(options.ignores ?? [])] },\n ZERO_SUPPRESSION,\n js.configs.recommended,\n ...(tseslint.configs.recommended as Linter.Config[]),\n {\n files: ['src/**/*.ts'],\n languageOptions: { globals: nodeGlobals },\n rules: {\n '@typescript-eslint/no-unused-expressions': ['error', { allowShortCircuit: true }],\n '@typescript-eslint/no-explicit-any': 'off',\n '@typescript-eslint/no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],\n 'no-undef': 'off',\n },\n },\n // noInlineConfig ignores inline /* global */ comments, so Node globals come from config.\n {\n files: ['**/*.mjs'],\n languageOptions: { globals: nodeGlobals, sourceType: 'module' },\n rules: { 'no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE] },\n },\n {\n files: ['lib/**/*.js', '**/*.js'],\n languageOptions: { globals: nodeGlobals, sourceType: 'commonjs' },\n rules: {\n 'no-undef': 'error',\n 'no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],\n },\n },\n ...(options.configs ?? []),\n prettierCompat,\n ];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/node/eslint.ts"],"names":[],"mappings":"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","sourcesContent":["import js from '@eslint/js';\nimport type { Linter } from 'eslint';\nimport prettierCompat from 'eslint-config-prettier';\nimport tseslint from 'typescript-eslint';\n\nexport interface EslintPresetOptions {\n ignores?: readonly string[];\n configs?: readonly Linter.Config[];\n}\n\nexport interface AppEslintPresetOptions extends EslintPresetOptions {\n a11y?: 'error' | 'warn';\n}\n\nconst ZERO_SUPPRESSION: Linter.Config = { linterOptions: { noInlineConfig: true } };\n\nconst APP_IGNORES = [\n '.next/**',\n 'node_modules/**',\n '.firebase/**',\n 'functions/**',\n // ESLint does not honor .gitignore, so the E2E harness's generated artifacts are ignored here.\n 'e2e-hosted-dev/.browser-profiles/**',\n 'e2e-hosted-dev/test-results/**',\n 'e2e-hosted-dev/failed-run-archive/**',\n 'test-results/**',\n 'out/**',\n 'dist/**',\n 'build/**',\n 'next-env.d.ts',\n '.next/types/**',\n '.next/dev/**',\n 'docs/**',\n];\n\nconst UNUSED_VARS_ALLOW_UNDERSCORE = {\n argsIgnorePattern: '^_',\n varsIgnorePattern: '^_',\n};\n\nconst APP_RULES: Linter.Config = {\n files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.mjs', '**/*.cjs'],\n // eslint-config-next registers react-hooks and flat config errors on a redefinition: rules only.\n languageOptions: {\n parserOptions: {\n ecmaVersion: 'latest',\n sourceType: 'module',\n ecmaFeatures: { jsx: true },\n },\n },\n rules: {\n // RULE-STD-MEDIA-101 bans next/image, the replacement this rule demands; served files render through the media renderer.\n '@next/next/no-img-element': 'off',\n '@typescript-eslint/no-unused-vars': [\n 'error',\n {\n ...UNUSED_VARS_ALLOW_UNDERSCORE,\n caughtErrorsIgnorePattern: '^_',\n destructuredArrayIgnorePattern: '^_',\n },\n ],\n 'react-hooks/rules-of-hooks': 'error',\n 'react-hooks/exhaustive-deps': 'error',\n 'react-hooks/set-state-in-effect': 'error',\n 'react-hooks/refs': 'error',\n 'react-hooks/immutability': 'error',\n 'react-hooks/incompatible-library': 'error',\n 'react-hooks/purity': 'error',\n },\n};\n\n// Type-aware: each TypeScript file is read through the nearest tsconfig.json that includes it.\nconst typeAwareNoAny = (files: string[]): Linter.Config => ({\n files,\n languageOptions: { parserOptions: { projectService: true } },\n rules: {\n '@typescript-eslint/no-unsafe-argument': 'error',\n '@typescript-eslint/no-unsafe-assignment': 'error',\n '@typescript-eslint/no-unsafe-call': 'error',\n '@typescript-eslint/no-unsafe-member-access': 'error',\n '@typescript-eslint/no-unsafe-return': 'error',\n },\n});\n\ntype RuleEntry = NonNullable<Linter.Config['rules']>[string];\n\n// eslint-config-next registers jsx-a11y and flat config errors on a second registration, so this\n// sets rules only; a severity-only entry keeps the options Next set (its Image alt-text options).\nfunction jsxA11yRecommended(\n nextConfigs: Linter.Config[],\n severity: 'error' | 'warn',\n): Linter.Config {\n const plugin = nextConfigs.find((config) => config.plugins?.['jsx-a11y'])?.plugins?.['jsx-a11y'];\n const recommended = (plugin?.configs?.recommended as Linter.Config | undefined)?.rules;\n if (!recommended) {\n throw new Error(\n 'eslint-config-next no longer registers eslint-plugin-jsx-a11y with a recommended config',\n );\n }\n const rules: Record<string, RuleEntry> = {};\n for (const [name, entry] of Object.entries(recommended)) {\n if (entry === undefined) continue;\n const level = Array.isArray(entry) ? entry[0] : entry;\n if (level === 'off' || level === 0) continue;\n rules[name] = Array.isArray(entry) ? [severity, ...entry.slice(1)] : severity;\n }\n return { files: ['**/*.tsx', '**/*.jsx'], rules };\n}\n\nexport async function createAppEslintConfig(\n options: AppEslintPresetOptions = {},\n): Promise<Linter.Config[]> {\n // Imported here: the Functions tree loads this module without eslint-config-next installed.\n const [{ default: nextCoreWebVitals }, { default: nextTypescript }] = await Promise.all([\n import('eslint-config-next/core-web-vitals'),\n import('eslint-config-next/typescript'),\n ]);\n return [\n { ignores: [...APP_IGNORES, ...(options.ignores ?? [])] },\n ZERO_SUPPRESSION,\n js.configs.recommended,\n ...(tseslint.configs.recommended as Linter.Config[]),\n ...(nextCoreWebVitals as Linter.Config[]),\n ...(nextTypescript as Linter.Config[]),\n jsxA11yRecommended(nextCoreWebVitals as Linter.Config[], options.a11y ?? 'error'),\n APP_RULES,\n typeAwareNoAny(['**/*.ts', '**/*.tsx']),\n ...(options.configs ?? []),\n prettierCompat,\n ];\n}\n\nexport async function createFunctionsEslintConfig(\n options: EslintPresetOptions = {},\n): Promise<Linter.Config[]> {\n // Imported here: the Next.js tree loads this module without globals installed.\n const { default: globals } = await import('globals');\n const nodeGlobals = { ...globals.node, ...globals.es2021 };\n return [\n { ignores: ['lib/**', 'build/**', ...(options.ignores ?? [])] },\n ZERO_SUPPRESSION,\n js.configs.recommended,\n ...(tseslint.configs.recommended as Linter.Config[]),\n {\n files: ['src/**/*.ts'],\n languageOptions: { globals: nodeGlobals },\n rules: {\n '@typescript-eslint/no-unused-expressions': ['error', { allowShortCircuit: true }],\n '@typescript-eslint/no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],\n 'no-undef': 'off',\n },\n },\n // noInlineConfig ignores inline /* global */ comments, so Node globals come from config.\n {\n files: ['**/*.mjs'],\n languageOptions: { globals: nodeGlobals, sourceType: 'module' },\n rules: { 'no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE] },\n },\n {\n files: ['lib/**/*.js', '**/*.js'],\n languageOptions: { globals: nodeGlobals, sourceType: 'commonjs' },\n rules: {\n 'no-undef': 'error',\n 'no-unused-vars': ['error', UNUSED_VARS_ALLOW_UNDERSCORE],\n },\n },\n typeAwareNoAny(['**/*.ts']),\n ...(options.configs ?? []),\n prettierCompat,\n ];\n}\n"]}
|
package/model-tiers.md
CHANGED
|
@@ -22,5 +22,5 @@ follows the session unless a doc names one.
|
|
|
22
22
|
task applies a fixed set of written patterns to many files; its prompt names every pattern and
|
|
23
23
|
makes anything outside them a stop that returns to the HIGH session that spawned it. That session
|
|
24
24
|
and its reviewer stay HIGH.
|
|
25
|
-
- **The
|
|
25
|
+
- **The session runs on the model DJ picks when he starts it.** Every SPAWNED
|
|
26
26
|
agent's tier is binding.
|
package/package.json
CHANGED
|
@@ -1306,7 +1306,7 @@ precedes any cap, gate, or counter.
|
|
|
1306
1306
|
**Rules:** RULE-STD-FRONTEND-203, RULE-STD-FRONTEND-204. **Scope:** `src`. Deep accessibility →
|
|
1307
1307
|
`ux-audit-playbook.md`.
|
|
1308
1308
|
```bash
|
|
1309
|
-
<GUARD_SUITE> -t "an icon-only button carries an accessible name|a button inside a form states its type|a display-only component is never a trigger or a control"
|
|
1309
|
+
<GUARD_SUITE> -t "an icon-only button carries an accessible name|a button inside a form states its type|a display-only component is never a trigger or a control|every dropdown carries an accessible name"
|
|
1310
1310
|
<GUARD_SUITE> -t "app code uses no transition-all and no Tailwind duration-\* utility|a smooth scroll is requested only by the effective-motion helper|every app stylesheet transition names its property and rides the motion tokens|one store reads the reduced-motion request"
|
|
1311
1311
|
rg -n "onClick=\{\(\)\s*=>\s*(void\s+)?(navigate|appNavigate)\(" src -g '*.tsx' -g '!**/__tests__/**'
|
|
1312
1312
|
rg -n "aria-label=\"(More actions|Show [Dd]etails|View [Dd]etails|Remove|Delete|Edit|Open)\"" src -g '*.tsx' -g '!**/__tests__/**'
|
|
@@ -1495,6 +1495,20 @@ stays reachable on the admin standing alone (step 70). No installed React packag
|
|
|
1495
1495
|
gated on admin standing.
|
|
1496
1496
|
**Status:** PASS · FAIL · NOT RUN · BLOCKED
|
|
1497
1497
|
|
|
1498
|
+
### 122. No promise rejection is discarded without a reason
|
|
1499
|
+
**Rules:** RULE-STD-ENG-009, RULE-STD-COMMENT-001, RULE-STD-QUALITY-007. **Scope:** every code tree,
|
|
1500
|
+
source and tests.
|
|
1501
|
+
```bash
|
|
1502
|
+
<GUARD_SUITE> -t "no promise rejection is discarded without a reason"
|
|
1503
|
+
```
|
|
1504
|
+
**Expected:** the guard is green: every `.catch()` whose handler does nothing — an empty body, or one
|
|
1505
|
+
that returns `undefined`, `void 0`, or `null` — carries a one-line comment saying why discarding the
|
|
1506
|
+
rejection is right (a constraint, or an exception naming its rule). Read each comment against its code:
|
|
1507
|
+
a discard of a failure a person or monitoring needs to see is the finding, whatever the comment says —
|
|
1508
|
+
it gets real handling. Each reviewed entry is a file whose discards genuinely cannot carry a comment;
|
|
1509
|
+
one whose file no longer discards is stale.
|
|
1510
|
+
**Status:** PASS · FAIL · NOT RUN · BLOCKED
|
|
1511
|
+
|
|
1498
1512
|
## Pass / Fail
|
|
1499
1513
|
|
|
1500
1514
|
Per [README.md](README.md) § Verdict.
|
package/playbooks/crosswalk.md
CHANGED
|
@@ -21,7 +21,7 @@ rule's coverage in its additions; they are not listed here.
|
|
|
21
21
|
| RULE-STD-ENG-006 | The backend is the security boundary | codebase-hygiene 23, 53, 77, 99, 101, 107; auth-boundary 8, 9, 12, 13; callable-functions 11, 12, 13; client-write-security-audit 2, 11; firestore-storage-rules 4, 5, 11; migration 7; testing-coverage 3 | `localOnlyVariables`, `envPublicValues` |
|
|
22
22
|
| RULE-STD-ENG-007 | Behavior, tests, and documentation move together | testing-coverage 2, 7 | — |
|
|
23
23
|
| RULE-STD-ENG-008 | Surface discovered problems | codebase-hygiene 25, 27, 73 | — |
|
|
24
|
-
| RULE-STD-ENG-009 | Fail truthfully and visibly | codebase-hygiene 60, 75, 83, 87, 88, 90; async-workflow 7; auth-boundary 14; callable-functions 9; data-integrity 13; migration 9; observability 3, 4, 6, 7, 8, 10; performance-audit 3; testing-coverage 11; ux-audit 11 |
|
|
24
|
+
| RULE-STD-ENG-009 | Fail truthfully and visibly | codebase-hygiene 60, 75, 83, 87, 88, 90, 122; async-workflow 7; auth-boundary 14; callable-functions 9; data-integrity 13; migration 9; observability 3, 4, 6, 7, 8, 10; performance-audit 3; testing-coverage 11; ux-audit 11 | `swallowedRejections` |
|
|
25
25
|
| RULE-STD-ENG-010 | Spawned agents inherit the rules | codebase-hygiene 69 — Designated manual review (step 69): verified on the spawning prompt, never by a search of the repo. | — |
|
|
26
26
|
| RULE-STD-ENG-011 | Once data is live, persisted shapes change with a migration path | client-write-security-audit 8; data-integrity 7; migration 3, 5 | — |
|
|
27
27
|
|
|
@@ -159,7 +159,7 @@ rule's coverage in its additions; they are not listed here.
|
|
|
159
159
|
| RULE-STD-QUALITY-003 | E2E is green only at zero failures AND zero retry-passes | testing-coverage 1, 6 | — |
|
|
160
160
|
| RULE-STD-QUALITY-004 | E2E tests use the established fixtures and web-first assertions | codebase-hygiene 28; migration 10; testing-coverage 6, 8, 10 | — |
|
|
161
161
|
| RULE-STD-QUALITY-006 | Lint and format: zero suppression in apps, reasoned disables in packages; one enforced format | codebase-hygiene 73; package-boundaries 7 | — |
|
|
162
|
-
| RULE-STD-QUALITY-007 | Guard tests enforce ownership and boundaries, never inventories | codebase-hygiene 35, 42, 63, 80, 83, 87, 88, 90, 91, 95, 96, 117, 118, 119, 120 | — |
|
|
162
|
+
| RULE-STD-QUALITY-007 | Guard tests enforce ownership and boundaries, never inventories | codebase-hygiene 35, 42, 63, 80, 83, 87, 88, 90, 91, 95, 96, 117, 118, 119, 120, 122 | — |
|
|
163
163
|
| RULE-STD-QUALITY-008 | Rules and E2E run against a fresh dev:e2e | codebase-hygiene 29; dependency-audit 3; firestore-storage-rules 10; testing-coverage 1, 9 | — |
|
|
164
164
|
| RULE-STD-QUALITY-009 | A test loads the code it tests when its file loads | codebase-hygiene 42, 118; package-boundaries 8 | `testCaseModuleLoads` |
|
|
165
165
|
| RULE-STD-QUALITY-010 | A test's time limit names a budget | codebase-hygiene 119; package-boundaries 10 | `testTimeouts` |
|
|
@@ -180,7 +180,7 @@ rule's coverage in its additions; they are not listed here.
|
|
|
180
180
|
|
|
181
181
|
| Rule | Title | Audited by (shared steps) | Guards |
|
|
182
182
|
|---|---|---|---|
|
|
183
|
-
| RULE-STD-COMMENT-001 | No comment unless the code cannot say it | codebase-hygiene 89 |
|
|
183
|
+
| RULE-STD-COMMENT-001 | No comment unless the code cannot say it | codebase-hygiene 89, 122 | `swallowedRejections` |
|
|
184
184
|
| RULE-STD-COMMENT-002 | No dates | codebase-hygiene 89 | — |
|
|
185
185
|
| RULE-STD-COMMENT-003 | No line numbers | codebase-hygiene 89 | — |
|
|
186
186
|
| RULE-STD-COMMENT-004 | No ticket names or work labels | codebase-hygiene 89 | — |
|
|
@@ -75,7 +75,7 @@ reachable through its accessible name or title.
|
|
|
75
75
|
**Rules:** RULE-STD-FRONTEND-203, RULE-STD-FRONTEND-204, RULE-STD-FRONTEND-003. **Scope:** the major
|
|
76
76
|
routes. **Method (axe and Lighthouse on the local dev server, plus a keyboard walkthrough).**
|
|
77
77
|
**Expected:** beyond the component-level lint rules — one `main` landmark and skip link owned by the
|
|
78
|
-
shell, with no page rendering its own `<main>`; icon-only buttons carry an accessible name, and a
|
|
78
|
+
shell, with no page rendering its own `<main>`; icon-only buttons and every dropdown carry an accessible name, and a
|
|
79
79
|
control repeated per item is named for its item; a custom choice button announces its selected state;
|
|
80
80
|
a search dropdown is a labeled combobox whose highlighted result is announced as it moves, with Enter
|
|
81
81
|
to pick and Escape to close, and two searches on one page never share an id; form fields associate
|
|
@@ -77,7 +77,7 @@ A test proves the business rule, never the code's current output: its expected v
|
|
|
77
77
|
### RULE-STD-QUALITY-006 — Lint and format: zero suppression in apps, reasoned disables in packages; one enforced format
|
|
78
78
|
**Scope.** both
|
|
79
79
|
|
|
80
|
-
**Rule.** The lint half is split by scope; the format half is the same for both. **Apps** are zero suppression: no `eslint-disable` comment anywhere in the linted trees — source, Functions, E2E, scripts, and config. When a rule fires, fix the root cause, and solve a genuine false positive structurally — rename, refactor, or scope the rule — never by suppressing the line; external-authority state pruning uses the sanctioned render-phase adjustment, not a suppressed effect. A `warn`-level finding is a finding to fix, and a rule is never turned off, nor `ignores` widened, to pass failing code. New app code also follows the conventions the lint config forces, listed below. **Packages** permit an `eslint-disable-next-line` only for a narrow intentional case, carrying its reason on the line; it is preferred over weakening the rule, and a real violation is fixed at its source.
|
|
80
|
+
**Rule.** The lint half is split by scope; the format half is the same for both. **Apps** are zero suppression: no `eslint-disable` comment anywhere in the linted trees — source, Functions, E2E, scripts, and config. When a rule fires, fix the root cause, and solve a genuine false positive structurally — rename, refactor, or scope the rule — never by suppressing the line; external-authority state pruning uses the sanctioned render-phase adjustment, not a suppressed effect. A `warn`-level finding is a finding to fix, and a rule is never turned off, nor `ignores` widened, to pass failing code. New app code also follows the conventions the lint config forces, listed below. **Packages** permit an `eslint-disable-next-line` only for a narrow intentional case where no fix works, on DJ's yes, carrying its reason on the line; it is preferred over weakening the rule, and a real violation is fixed at its source.
|
|
81
81
|
|
|
82
82
|
Every code file — source, tests, scripts, and config — is in the one format Prettier writes from the shared Prettier config (this package's `config/prettier.json`); `test:quiet`'s format stage writes that format and then checks it, a check-only run (a package release preflight, `test:all`) only checks, so a release never publishes formatting nobody committed, and the check fails a file that is not formatted; `eslint-config-prettier` keeps ESLint out of formatting. A file is formatted by running Prettier, never reflowed by hand.
|
|
83
83
|
|
|
@@ -92,14 +92,14 @@ Every code file — source, tests, scripts, and config — is in the one format
|
|
|
92
92
|
- An anonymous React wrapper component returned from a test helper (assign `displayName`).
|
|
93
93
|
- Straight `'`/`"` in JSX text content (use `'`/`"`).
|
|
94
94
|
- Suppressing `exhaustive-deps` instead of fixing at the source (destructure the stable `mutate`; memoize a derived dep; destructure the specific prop fields used).
|
|
95
|
-
- An unformatted code file; a `prettier-ignore` without its reason; an ESLint formatting rule that fights Prettier.
|
|
95
|
+
- An unformatted code file; a `prettier-ignore` without its reason, or added when a fix works or without DJ's yes; an ESLint formatting rule that fights Prettier.
|
|
96
96
|
|
|
97
97
|
**References.** RULE-STD-ENG-008; this package's `node/eslint` bases and `config/prettier.json`; each repo's ESLint config and `.prettierignore`.
|
|
98
98
|
|
|
99
99
|
### RULE-STD-QUALITY-007 — Guard tests enforce ownership and boundaries, never inventories
|
|
100
100
|
**Scope.** both
|
|
101
101
|
|
|
102
|
-
**Rule.** Structural guard tests (the repo-guards suite each repo runs in its gate — an app's guard suite, ttt-packages' boundary suite) enforce a forbidden boundary or single-owner rule against the current clean baseline — every allowlist entry carrying a one-line justification — so that a NEW regression fails the build. A guard must not mirror an exhaustive list of today's files, exports, packages, or domains: an inventory-mirror breaks on every legitimate addition and audits nothing.
|
|
102
|
+
**Rule.** Structural guard tests (the repo-guards suite each repo runs in its gate — an app's guard suite, ttt-packages' boundary suite) enforce a forbidden boundary or single-owner rule against the current clean baseline — every allowlist entry carrying a one-line justification — so that a NEW regression fails the build. An entry is added only when no fix works, on DJ's yes, with its reason in the entry. A guard must not mirror an exhaustive list of today's files, exports, packages, or domains: an inventory-mirror breaks on every legitimate addition and audits nothing.
|
|
103
103
|
|
|
104
104
|
**Why.** A brittle inventory-mirror is churn, not enforcement; the value is catching the next violation, not counting the current files.
|
|
105
105
|
|
|
@@ -127,7 +127,7 @@ Every code file — source, tests, scripts, and config — is in the one format
|
|
|
127
127
|
### RULE-STD-QUALITY-009 — A test loads the code it tests when its file loads
|
|
128
128
|
**Scope.** both
|
|
129
129
|
|
|
130
|
-
**Rule.** A test imports the code it tests at the top of its file — a static import, or a top-level `await import()` — never inside a case, a hook, or a helper they call, and never resets the module registry. A case that needs other state gets it without a reload: an environment the code reads at load is set in `vi.hoisted` before the import, per-instance state is cleared through the module's test-only `__reset…()` seam, and a flag the code reads once is read when it is called instead. A file whose fresh module per case is itself what it proves is a reviewed entry with its why, and an entry whose file no longer does is stale. The repo-guards `testCaseModuleLoads` guard enforces it.
|
|
130
|
+
**Rule.** A test imports the code it tests at the top of its file — a static import, or a top-level `await import()` — never inside a case, a hook, or a helper they call, and never resets the module registry. A case that needs other state gets it without a reload: an environment the code reads at load is set in `vi.hoisted` before the import, per-instance state is cleared through the module's test-only `__reset…()` seam, and a flag the code reads once is read when it is called instead. A file whose fresh module per case is itself what it proves is a reviewed entry with its why — an entry is added only when no fix works, on DJ's yes, with its reason in the entry — and an entry whose file no longer does is stale. The repo-guards `testCaseModuleLoads` guard enforces it.
|
|
131
131
|
|
|
132
132
|
**Why.** A module's first load pulls its whole graph; inside a case that cold load is timed as the case's own work, so a file's first case was slow for nothing it did and, under the full parallel run, crossed its timeout.
|
|
133
133
|
|
|
@@ -13,21 +13,25 @@ description: Claude Code workflow (Build or fix). Use when the work is big enoug
|
|
|
13
13
|
|
|
14
14
|
## Before anything spawns
|
|
15
15
|
|
|
16
|
-
1. The normal preflight
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
1. The normal preflight. This session saves its context for managing agents and does not read or
|
|
17
|
+
edit code itself (`<DEV_STANDARDS>/development-preferences.md` § Agents).
|
|
18
|
+
2. Launch read-only agents to read the routed rules, the `docs/design/*.md` for every area involved,
|
|
19
|
+
and the current code, and report the system back; the design each building agent gets comes from
|
|
20
|
+
their reports.
|
|
21
|
+
3. Propose to DJ: the number of agents, each one's tier, and each one's exact scope and owned files. Nothing spawns
|
|
19
22
|
without DJ's yes.
|
|
20
23
|
4. App-side work: confirm every shared package it depends on is at `@latest` first. Package work and
|
|
21
24
|
app work never run at the same time — all package work, its gate, and DJ's publish come first.
|
|
22
25
|
|
|
23
26
|
## Every spawned prompt
|
|
24
27
|
|
|
28
|
+
- The high-level picture and the design to build: build this, this way.
|
|
25
29
|
- The agent's exact scope and owned files, and its mandatory reading as explicit read-before-acting
|
|
26
30
|
paths: the routed rule files (shared and the app's) and `docs/design/*.md` for its area
|
|
27
31
|
(RULE-STD-ENG-010).
|
|
28
32
|
- A hard ban on spawning agents.
|
|
29
|
-
- A kill-and-escalate clause: a needed package change,
|
|
30
|
-
this session.
|
|
33
|
+
- A kill-and-escalate clause: a needed package change, a better or different way than the design,
|
|
34
|
+
or anything unexpected → stop and report to this session.
|
|
31
35
|
- Targeted tests only, on its own work.
|
|
32
36
|
- Tier per `<DEV_STANDARDS>/model-tiers.md` (implementation agents; a mechanical-only part uses
|
|
33
37
|
`mechanical-fan-out.md` instead).
|
|
@@ -37,7 +41,7 @@ description: Claude Code workflow (Build or fix). Use when the work is big enoug
|
|
|
37
41
|
- Adversarial review agents at the HIGH tier over the landed work, each also checking every new or
|
|
38
42
|
changed test against the business rule it claims to prove (RULE-STD-QUALITY-001; testing-coverage
|
|
39
43
|
playbook).
|
|
40
|
-
- Exactly ONE final full gate after every agent has landed: the Normal `<APP_NAME>` Test Flow
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
- Exactly ONE final full gate after every agent has landed: the Normal `<APP_NAME>` Test Flow, run
|
|
45
|
+
as `<DEV_STANDARDS>/claude/CLAUDE.base.md` § The Normal `<APP_NAME>` Flows says for an
|
|
46
|
+
orchestrator.
|
|
43
47
|
- Delete the finished `docs/work/` items and the ledger, then stop for DJ's push.
|
|
@@ -25,7 +25,9 @@ description: Claude Code workflow (Build or fix). Use when DJ wants known, decid
|
|
|
25
25
|
Normal `<APP_NAME>` Dev Flow (`<DEV_STANDARDS>/claude/CLAUDE.base.md`).
|
|
26
26
|
- Every fix ships a regression test that fails before the fix and proves the business rule
|
|
27
27
|
(RULE-STD-QUALITY-001). Design docs the fix changes are updated in the same change.
|
|
28
|
-
- A
|
|
28
|
+
- A piece of the work agents would do better → propose LOW or HIGH agents for it to DJ, staying
|
|
29
|
+
inline (`<DEV_STANDARDS>/development-preferences.md` § Agents); a large mechanical change uses
|
|
30
|
+
`mechanical-fan-out.md`.
|
|
29
31
|
|
|
30
32
|
## Finishing
|
|
31
33
|
|
|
@@ -23,8 +23,9 @@ description: Claude Code workflow (Build or fix). Use when a multi-task plan DJ
|
|
|
23
23
|
|
|
24
24
|
- Packages-first: every package-side task in the plan lands, gates, and is released before any
|
|
25
25
|
app-side task (Normal `<APP_NAME>` Dev Flow, `<DEV_STANDARDS>/claude/CLAUDE.base.md`).
|
|
26
|
-
- Build inline.
|
|
27
|
-
|
|
26
|
+
- Build inline. A piece of the work agents would do better → propose LOW or HIGH agents for it to
|
|
27
|
+
DJ, staying inline (`<DEV_STANDARDS>/development-preferences.md` § Agents); large mechanical parts
|
|
28
|
+
use `mechanical-fan-out.md`.
|
|
28
29
|
- Approval covers exactly the approved tasks — no adjacent refactors.
|
|
29
30
|
- Every task ships its regression test (RULE-STD-QUALITY-001) and its doc updates.
|
|
30
31
|
|
|
@@ -34,5 +34,5 @@ through this; it stays inline or goes to `fan-out-implementation.md`.
|
|
|
34
34
|
5. **One HIGH reviewer** checks all the agents' changes together once every agent is done: each
|
|
35
35
|
change matches a listed pattern, nothing outside the patterns slipped in, and every new or changed
|
|
36
36
|
test proves the business rule it claims, not the code's current output (RULE-STD-QUALITY-001).
|
|
37
|
-
6. **One final gate.** Back in the calling workflow: the
|
|
38
|
-
|
|
37
|
+
6. **One final gate.** Back in the calling workflow: the inline session runs the Normal Test Flow
|
|
38
|
+
once, for the whole change set.
|
|
@@ -21,8 +21,9 @@ description: Claude Code workflow (Build or fix). Use when DJ names a step of a
|
|
|
21
21
|
3. Work the step, or one focused slice of it. A step with a publish stop: ALL its package work first,
|
|
22
22
|
then the release command, per the Normal `<APP_NAME>` Dev Flow
|
|
23
23
|
(`<DEV_STANDARDS>/claude/CLAUDE.base.md`).
|
|
24
|
-
4.
|
|
25
|
-
|
|
24
|
+
4. A piece of the work agents would do better → propose LOW or HIGH agents for it to DJ, staying
|
|
25
|
+
inline (`<DEV_STANDARDS>/development-preferences.md` § Agents); large mechanical parts use
|
|
26
|
+
`mechanical-fan-out.md`.
|
|
26
27
|
5. The Normal `<APP_NAME>` Test Flow, once, on the change set.
|
|
27
28
|
6. Delete each finished item from the step's work doc. When the step is done, delete its folder and
|
|
28
29
|
its section in `PLAN.md` — never renumber.
|