Build and deploy / build-and-deploy (push) Failing after 4s
Pouya's rulings of 2026-09-03 (the last two D20 findings) and 2026-09-04 (the spam observation and four mitigations), in one change set. D20 finding 10 — med-arb is billed BY PHASE, each phase at the rates already published, so /fees/'s "Every figure is on this page" is true as written rather than narrowed. FEES.medArb is the single source; docs/07 §Med-arb carries the rule INTERIM against R5, and R5 now carries it back, because a derived price moves silently when a rate moves. D20 finding 13 — conduct undertaking (g), attested 2026-09-03, published as his wording verbatim on /legal/privacy/ and /contact/. The clause that raised the finding promised to DISCLOSE a conflicts check's outcome, which the attestation does not cover; it is struck. D20 now partitions 17 fixed / 2 refuted / 1 owed. Spam, 2026-09-04 — recorded in docs/05 §Observed abuse with the date and signature. A second honeypot (a decoy checkbox, own class, `hidden`, a label that tells a human not to tick it) and scoring that LABELS and never rejects: nothing is dropped, nothing new is stored, and only the operator notification changes. Q65 opens the WAF cost call. The timing floor could not be built: there is no timing check and never has been. docs/05 carries it struck, and every mechanism that would give a real per-visitor clock breaks zero-JS, handler-and-form-only, or D1. Q66. configure.mjs gains section 5 — a custom origin request policy forwarding CloudFront-Viewer-Address on /api/*. Written, dry-run against the live distribution, NOT applied. It reads the handler's own header reads and refuses to run if the whitelist omits one. And reading the live account to do it found four AGENTS.md §7 rows saying the intake backend was undeployed, two days after it went live — corrected against get-function-configuration, get-routes, get-stage, get-policy and the deployed zip, which was downloaded and read. Review: adversarial-reviewer only (claims-auditor is D20's cutover pass and has run). Round 1 five lenses, 56 findings, 7 blocking, 4 refuted by an independent refuter; round 2 four lenses, 36 findings, 33 of them defects in round 1's own repairs. Stopped at two per D19. Gates, exit status read for each: check 0 · build 0 (23 pages) · check:claims 0 · check:intake 0 · og:proof 0 · lint 0 · spam-score.test 39/39 with 6/6 mutations killed · router.test 30/30 · minifier grep 1 (clean) · lighthouse 0, no category below 95 · configure.mjs dry run 0, nothing written. Nothing deployed and nothing applied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
117 lines
5.1 KiB
JavaScript
117 lines
5.1 KiB
JavaScript
// ESLint 10 flat config. Scope is deliberately small: this project targets zero
|
|
// client JavaScript (CLAUDE.md, AGENTS.md §7), so the only JS/TS here is build
|
|
// configuration, site data, and the occasional island. Rules exist to catch
|
|
// mistakes, not to impose style — Prettier owns formatting.
|
|
//
|
|
// `typescript-eslint` is here because .astro frontmatter IS TypeScript, so the
|
|
// plugin cannot parse a single component without it. It runs unconfigured for
|
|
// type-awareness on purpose: `astro check` already does the type checking, and
|
|
// duplicating it here would be slower and would disagree at the edges.
|
|
|
|
import js from '@eslint/js';
|
|
import globals from 'globals';
|
|
import tseslint from 'typescript-eslint';
|
|
import astro from 'eslint-plugin-astro';
|
|
|
|
export default [
|
|
{ ignores: ['dist/**', 'node_modules/**', '.astro/**', 'docs/reference/**'] },
|
|
|
|
js.configs.recommended,
|
|
...tseslint.configs.recommended,
|
|
...astro.configs.recommended,
|
|
...astro.configs['flat/jsx-a11y-recommended'],
|
|
|
|
// `no-undef` off for TYPESCRIPT ONLY, on typescript-eslint's own advice: it
|
|
// has no type information, so every ambient global is a false positive —
|
|
// Astro declares `ImageMetadata`, `astroHTML.JSX` and friends globally, and
|
|
// .astro frontmatter IS TypeScript. tsc catches a real undefined reference,
|
|
// which is what `npm run check` is for.
|
|
//
|
|
// NOT applied to .js/.mjs. `tsconfig.json` sets `allowJs` without `checkJs`,
|
|
// so plain JS is not type-checked by anything — turning the rule off there
|
|
// meant a typo like `procss.env.X` in astro.config.mjs passed lint silently.
|
|
{
|
|
files: ['**/*.ts', '**/*.astro'],
|
|
rules: { 'no-undef': 'off' },
|
|
},
|
|
|
|
{
|
|
files: ['**/*.{js,mjs,ts}', '**/*.astro'],
|
|
languageOptions: {
|
|
ecmaVersion: 2023,
|
|
sourceType: 'module',
|
|
globals: { ...globals.browser, ...globals.node },
|
|
},
|
|
rules: {
|
|
// A stray console.log in a static build is dead weight shipped to nobody.
|
|
'no-console': ['warn', { allow: ['warn', 'error'] }],
|
|
|
|
// `role="list"` on a <ul> is redundant to a spec reader and load-bearing
|
|
// in a browser: Safari drops list semantics from any list styled
|
|
// `list-style: none`, so VoiceOver stops announcing "list, 6 items".
|
|
// src/styles/global.css keys its own reset off `ul[role='list']` for
|
|
// exactly this reason. The rule is right in general; this is the one
|
|
// documented exception, and it is scoped to that single pairing.
|
|
'astro/jsx-a11y/no-redundant-roles': [
|
|
'error',
|
|
{ ul: ['list'], ol: ['list'] },
|
|
],
|
|
eqeqeq: ['error', 'always'],
|
|
'prefer-const': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{ argsIgnorePattern: '^_' },
|
|
],
|
|
},
|
|
},
|
|
|
|
// `scripts/` ARE CLI TOOLS, AND PRINTING IS THEIR OUTPUT. The `no-console`
|
|
// rule above is justified in this config as "a stray console.log in a static
|
|
// build is dead weight shipped to nobody" — which is a statement about the
|
|
// shipped bundle, and nothing in `scripts/` reaches it. `check-claims.mjs`
|
|
// exists to print what it matched: CLAUDE.md's rule is that a grep is not a
|
|
// finding until you read what it matched, so suppressing its output would
|
|
// defeat the tool. Scoped to this directory rather than disabled globally.
|
|
//
|
|
// ⚠️ IT MUST SIT AFTER THE BLOCK IT OVERRIDES. Flat config applies matching
|
|
// blocks in order, last one wins — placed above, this had no effect at all
|
|
// and `npm run lint` still reported all six warnings. Measured, not assumed.
|
|
{
|
|
files: ['scripts/**/*.{js,mjs}'],
|
|
rules: { 'no-console': 'off' },
|
|
},
|
|
|
|
/* `infra/cloudfront/` IS NOT A NODE MODULE AND NOT A BROWSER SCRIPT. A
|
|
CloudFront Function's entry point is a bare `function handler(event)` that
|
|
the runtime calls **by name** — it has no `export` (the runtime rejects
|
|
module syntax) and nothing in the file references it, so
|
|
`no-unused-vars` fires on the one declaration that is the whole point of
|
|
the file. `argsIgnorePattern` cannot reach a function declaration, so the
|
|
rule is scoped off here rather than silenced with a comment at the
|
|
declaration, which would read as though the name were incidental.
|
|
The test beside it is a CLI tool and prints, exactly as `scripts/` does.
|
|
|
|
⚠️ LIKE THE BLOCK ABOVE, THIS MUST STAY LAST. Flat config applies matching
|
|
blocks in order and the last one wins. */
|
|
{
|
|
files: ['infra/cloudfront/**/*.{js,mjs}'],
|
|
rules: {
|
|
'@typescript-eslint/no-unused-vars': 'off',
|
|
'no-console': 'off',
|
|
},
|
|
},
|
|
|
|
/* THE BACKEND TEST FILE ONLY — NOT `backend/intake/**`. `handler.mjs` runs in
|
|
Lambda, where `console.log` is a line in CloudWatch that nobody reads and
|
|
`console.warn`/`console.error` are the two that signal, so the rule stays on
|
|
for it deliberately. The test beside it is a CLI tool and prints its verdict,
|
|
exactly as `scripts/` and the router test do.
|
|
|
|
⚠️ LAST, LIKE THE TWO ABOVE. Flat config applies matching blocks in order
|
|
and the last one wins. */
|
|
{
|
|
files: ['backend/**/*.test.mjs'],
|
|
rules: { 'no-console': 'off' },
|
|
},
|
|
];
|