Build and deploy / build-and-deploy (push) Failing after 4s
Five items of Pouya's production run, 2026-09-01.
Q61 — scroll-padding-top becomes a max() ramp on `10lh - 83px`, with the
plain calc() first as the fallback for engines without `lh`. Hidden focus
stops under minimumFontSize=32: 290 of 1,455 -> 0, control build still
290. Default settings byte-identical (0 differences over 352 page-widths x
17 fields). The 12 residual cells at minimumFontSize=16/20 are pre-existing
and unchanged-or-better; reported, not widened, per instruction.
Intake backend + CloudFront — docs/09-cutover-runbook.md is the
copy-paste sequence for admin execution: every command followed by its
verification and expected output, rollback per part, and Part 10 is Q60's
TTL test. infra/cloudfront/router.js is the trailing-slash function
(30-case suite; 8 fail against the pre-review version, incl. a
protocol-relative open redirect). infra/cloudfront/configure.mjs is
dry-run-by-default and idempotent. scripts/intake-env.mjs emits the six
Lambda env vars from src/data/site.ts.
Four launch blockers found by reading the running system:
- handler.mjs wrote pk/sk; the live table's key is submissionId with no
sort key, so every submission would have failed validation silently
- the Lambda invoke permission is scoped to the old route path
- 22 of 23 pages 403 without the router function
- there was no 404 page; src/pages/404.astro adds it
Claims audit (D20 cutover pass) — five gloss over-reaches corrected on
/practice/energy/, /practice/insurance/ (x2), /practice/technology/ and
/med-arb/. Three findings left open for Pouya: Q62, the /med-arb/ gloss,
and Q60.
Q62 — one frozen-tripwire pattern added under the freeze's own breach
exception, with a probe and four negative fixtures. check:claims exits 1
until the false /legal/privacy/ sentence is corrected, so both deploy
paths are blocked by a mechanism rather than by memory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Md3GndFqWPzK78xAoebsg5
104 lines
4.5 KiB
JavaScript
104 lines
4.5 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',
|
|
},
|
|
},
|
|
];
|