@ttt-productions/dev-standards 0.0.4 → 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/development-preferences.md +23 -14
- 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/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/workflows/claude/fan-out-implementation.md +1 -1
|
@@ -67,11 +67,12 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
67
67
|
verified and a recommendation, and stays open for DJ. When DJ has said he is away, no question
|
|
68
68
|
blocks: it goes in chat, the session finishes every part that does not depend on it, and moves to
|
|
69
69
|
the next item.
|
|
70
|
-
- **No
|
|
71
|
-
|
|
72
|
-
the
|
|
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
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
|
|
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.
|
|
75
76
|
- When DJ asks whether the conversation is good to end, or says to start a new one, the session first
|
|
76
77
|
says how many questions are open and asks whether to answer them here or move them into the new
|
|
77
78
|
conversation's prompt.
|
|
@@ -145,6 +146,13 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
145
146
|
- Verify at the cost of the fix: a one-file fix runs its single file first; the broad gate only
|
|
146
147
|
when the blast radius warrants it. Never run multiple full repository gates concurrently.
|
|
147
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.
|
|
148
156
|
|
|
149
157
|
## Engineering judgment
|
|
150
158
|
|
|
@@ -157,13 +165,14 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
157
165
|
- No half-working, dead, or known-bad code — build it or remove it. On a second sighting of a defect
|
|
158
166
|
class, tell DJ and propose killing the CLASS system-wide plus a regression guard; do it only on
|
|
159
167
|
DJ's command.
|
|
160
|
-
- **Never make yourself an exception.** Code that breaks a rule, guard, or
|
|
161
|
-
exception is asked for only when there is genuinely no other way, and it goes to DJ with
|
|
162
|
-
reason, why no fix works. Nothing is exempted without his yes. Every exception he approves carries
|
|
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
|
|
163
171
|
its reason at the exception itself, so anyone reading it years later knows at once why it exists:
|
|
164
|
-
the guard entry's reason field, the comment on the `eslint-disable` / `prettier-ignore`
|
|
165
|
-
for a design-level exception, the design doc it
|
|
166
|
-
exists, never who approved it.
|
|
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.
|
|
167
176
|
- Reviewing an external plan (ChatGPT/Codex/etc.) means answering "is this the BEST code to solve
|
|
168
177
|
my problem" — never just verifying its claims. Evaluate the design space (alternatives, root cause,
|
|
169
178
|
structure) and fold an optimality verdict into the reviewed doc.
|
|
@@ -191,8 +200,8 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
191
200
|
No doc, plan, ledger, or prompt makes a conversation an orchestrator.
|
|
192
201
|
- **Inline:** the session does the research and the work itself. When a piece of the work would be
|
|
193
202
|
done better by agents — LOW for mechanical work applied from written examples, HIGH for thinking
|
|
194
|
-
work or review — it proposes them to DJ
|
|
195
|
-
only on his yes. Launching agents, a predefined wave included, never makes it an orchestrator; it
|
|
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
|
|
196
205
|
still runs its own Normal Test Flow.
|
|
197
206
|
- **Orchestrator:** the session saves its context for managing agents and does not read or edit code
|
|
198
207
|
itself. It first launches read-only agents to understand the system, then gives each building
|
|
@@ -203,8 +212,8 @@ shared copy, in `@ttt-productions/dev-standards`, that every repo reads.
|
|
|
203
212
|
- The main session owns edits unless DJ explicitly approves parallel implementers with disjoint file
|
|
204
213
|
ownership. Nothing spawns without DJ's yes.
|
|
205
214
|
- Every prompt that spawns an agent names that agent's mandatory reading as explicit
|
|
206
|
-
read-before-acting paths (RULE-STD-ENG-010), hard-bans spawning further agents
|
|
207
|
-
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
|
|
208
217
|
session.
|
|
209
218
|
- How a given kind of conversation runs its agents is its development workflow's procedure
|
|
210
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/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
|
|
@@ -18,7 +18,7 @@ description: Claude Code workflow (Build or fix). Use when the work is big enoug
|
|
|
18
18
|
2. Launch read-only agents to read the routed rules, the `docs/design/*.md` for every area involved,
|
|
19
19
|
and the current code, and report the system back; the design each building agent gets comes from
|
|
20
20
|
their reports.
|
|
21
|
-
3. Propose to DJ: the number of agents and each one's exact scope and owned files. Nothing spawns
|
|
21
|
+
3. Propose to DJ: the number of agents, each one's tier, and each one's exact scope and owned files. Nothing spawns
|
|
22
22
|
without DJ's yes.
|
|
23
23
|
4. App-side work: confirm every shared package it depends on is at `@latest` first. Package work and
|
|
24
24
|
app work never run at the same time — all package work, its gate, and DJ's publish come first.
|