Repository navigation
Choose the tech stacks #3
Description
Activity
- addedtype:spikeResearch that answers a question and ends thereResearch that answers a question and ends theretrack:fullNeeds evidence: brief, research, plan, execute, reviewNeeds evidence: brief, research, plan, execute, reviewstage:briefBeing briefedBeing briefed
on Oct 7, 2026 Time: issue-3
Generated by
worklog renderfromtime.jsonl. Do not edit by hand.Step Estimate Started Finished Wall Active Sessions brief 30m 2026-10-07 00:01 2026-10-07 00:03 2m 2m 3da8d26d (claude-code) research 3h 2026-10-07 00:05 2026-10-07 00:41 22m 22m 3da8d26d (claude-code) Total 3h 30m 24m 24m Active time counts agent turns plus up to 5 minutes of each wait for a person, and no single gap
counts more than 30 minutes.—means nothing was recorded.workflow · claude-code · 2026-10-07 23:03
- addedstage:researchBeing researchedBeing researchedand removedstage:briefBeing briefedBeing briefed
on Oct 7, 2026 Research: Choose the tech stacks (shared baseline)
Status: Answered · Evidence base:
d0573a0(clean), plus each child at the SHA in its spike · 2026-10-07Summary
The shared baseline for every repository:
- Runtime: Node.js LTS everywhere. Node 24 now, and Node 26 once it becomes LTS on 2026-10-28.
- Package manager: the npm that ships with Node, locked down in
.npmrc. It is the only option that blocks install scripts and new releases and still has full Dependabot security coverage. Dependabot doesn't support pnpm 11+, npm 12 or Bun security updates. - Toolchain (the design system publishes it): ESLint 10 with typescript-eslint and eslint-plugin-vue, Prettier, vue-tsc on TypeScript 6.0, Stylelint, Vitest and Knip.
- In the experiment, ESLint was the only linter that checked both Vue templates and type-aware rules.
- TypeScript 7 can't run vue-tsc or typescript-eslint yet.
- CI security, adopt eight checks:
- CodeQL for the app languages;
- a policy that pins actions by SHA;
- Dependabot with groups and a cooldown;
- push protection;
- dependency review;
- hadolint;
- Grype;
- SBOMs with attestations.
- CI security, reject:
- Trivy, which was compromised in March 2026;
- SonarQube Cloud;
- the tools that duplicate these.
- Containers: multi-stage builds onto distroless or nginx-unprivileged; non-root, read-only, all capabilities dropped, and
init: true. In the experiment, Node running as PID 1 ignored SIGTERM. - Market signal: a small sample of job postings slightly favours Node/TypeScript behind Vue. Rust, Ionic and Capacitor didn't appear.
Confidence is high, except the package manager (medium) and the market sample (low-medium).
Answers
RQ Answer Confidence RQ-1 Node.js LTS: Node 24 until Node 26 becomes LTS on 2026-10-28; it is supported to 2029-04-30. Package manager: the bundled npm 11, locked down in .npmrc:
•strict-allow-scripts, plus anallowScriptspolicy;
•min-release-age;
•allow-git=noneandallow-remote=none.
Dependabot supports npm v7–v11 and pnpm v7–v10, and gives Bun no security updates. pnpm 10 is the runner-up. Bun and Deno are rejected: Bun has no LTS and Deno would be a second runtime.High (runtime) / medium (package manager) RQ-2 ESLint 10 flat config with typescript-eslint recommendedTypeCheckedand eslint-plugin-vue, plus Prettier 3, vue-tsc 3 on TypeScript 6.0.x, Stylelint (standard-vue), Vitest 5 with coverage thresholds, and Knip.
Of 12 seeded problems, ESLint caught 9 with its presets and 11 with three extra rules; Biome (full Vue mode) caught 7 and Oxlint 3, with no template rules. TypeScript 7 runs neither vue-tsc nor typescript-eslint.High RQ-3 Adopt:
• CodeQLjavascript-typescriptplus the backend language;
• an org policy that pins actions by SHA;
• Dependabot (npm, docker, compose, and the backend ecosystem) with groups and a 7-day cooldown;
• push protection;
• dependency-review-action;
• hadolint;
• Grype;
•buildx --sbomplusactions/attest.
Reject: audit and OSV-Scanner (duplicates), Trivy (compromised March 2026), Docker Scout (one free repo), Scorecard and zizmor as CI jobs (they overlap CodeQL's actions queries), syft and cosign (BuildKit and attest cover them).High RQ-4 Build multi-stage on Debian trixie-slim and run on distroless nonroot: nodejs for Node, static or cc for Go or Rust. Static SPAs run onnginx-unprivileged. Every container gets:
• a numericUSERand exec-form CMD;
• a HEALTHCHECK;
•init: true;
•read_onlywith a/tmptmpfs;
•cap_drop: ALLandno-new-privileges.
Docker Hardened Images are the fallback. Chainguard's free tier (:latestonly) and Alpine (musl) are rejected.High RQ-5 No hosted quality gate. ESLint, vue-tsc, Vitest coverage thresholds and CodeQL already gate quality. SonarQube Cloud duplicates them and supports TypeScript only up to 5.9.3. Revisit Codecov only if reviews need coverage deltas. Medium-high RQ-6 Sample: 39 US Vue/Nuxt postings. Backend mentions: Node/TS 56%, Python 36%, Java 31%, .NET 28%, Go 8%, Rust 0%. Mobile: 8%, always "preferred"; Ionic appeared in 1 posting, Capacitor in none. Node/TS slightly wins a tie; nothing supports adding a mobile app. Low-medium (small sample) Recommendation
1. Runtime and package manager (F1-F12)
- Node.js LTS for builds, Nuxt SSR and a TS backend if one is chosen. Set
engines.nodeand.nvmrcto 24 now, and to 26 from 2026-10-28. - npm, the version bundled with Node. Its
.npmrc:strict-allow-scripts=true, with anallowScriptspolicy inpackage.json;min-release-age=7, plusmin-release-age-exclude[]=@<scope>/*;allow-git=noneandallow-remote=none;engine-strict=true.
npm ciin CI and in Docker builds. The design system publishes through npm trusted publishing, which adds provenance automatically.- Move to npm 12, which blocks scripts by default, once Dependabot lists it.
- This settles the checkout spike's "npm 12 or pnpm" question (commerce-checkout#1 RQ-5): npm 12 isn't on Dependabot's supported list yet.
- Options considered:
- pnpm 10 (runner-up): Dependabot supports it, it blocks scripts by default and it is strict about dependencies, but it is two majors behind.
- pnpm 11/12: better defaults, but Dependabot doesn't list them, so we'd need Renovate as well.
- Bun: no Dependabot security updates, no LTS, and it trusts its own allowlist.
- Deno: a second runtime under Node-first tooling.
2. The toolchain the design system publishes (F13-F30):
@rsl/eslint-config,@rsl/tsconfig,@rsl/prettier-configand@rsl/stylelint-config.Tool Configuration ESLint 10 @vue/eslint-config-typescriptrecommendedTypeChecked, eslint-plugin-vueflat/recommended,eqeqeq,prefer-const,vue/no-v-html: error, witheslint-config-prettierlastPrettier 3 defaults; oxfmt's output was byte-identical, so watch it vue-tsc 3 peer typescript: ~6.0until TypeScript 7.1 ships its API and the Vue tools support itStylelint 17 stylelint-config-standard-vueVitest 5 coverage-v8with thresholdsKnip 6 unused files, dependencies and exports Watch rather than adopt: Oxlint running ahead of ESLint (the way create-vue sets it up), and Biome once its Vue support is no longer experimental.
3. CI security (F31-F43)
- CodeQL: add
javascript-typescript(it covers.vue) and the backend language. - An org policy requiring SHA-pinned actions, with Dependabot keeping the SHAs current. The existing
@v7and@v4references are re-pinned first. - Dependabot:
npm,docker,docker-compose, andgomodorcargoif the backend uses one. Group minor and patch updates, withcooldown.default-days: 7. - Push protection. Also add an Authorize.Net pattern to git-secrets, because neither tool knows one.
- dependency-review-action with
fail-on-severityandallow-licenses. - hadolint.
- Grype through
anchore/scan-action, uploading SARIF and failing on high. Dependabot's docker ecosystem has no security updates, so Grype is what sees base-image CVEs. docker buildx build --sbom=trueplusactions/attest.
4. Container baseline (F44-F52)
Workload Build → run Storefront SSR, and a TS backend node:<lts>-trixie-slim→gcr.io/distroless/nodejs<lts>-debian13:nonroot(about 55 MB compressed)Admin, checkout (static) nginxinc/nginx-unprivileged:alpine-slim(about 8 MB compressed, uid 101, port 8080), pinned by digest; nginx sends the CSPGo distroless/static-debian13:nonrootRust distroless/cc-debian13:nonrootEvery service:
- a numeric
USERand an exec-form CMD; - a HEALTHCHECK, which in distroless is a Node script, not curl;
- in Compose:
init: true,read_only: truewith a/tmptmpfs,cap_drop: [ALL]andsecurity_opt: [no-new-privileges:true]; - an application that drains connections on SIGTERM.
The spikes report image sizes two ways: compressed figures here, uncompressed in the storefront and checkout reports.
5. Quality gate: none hosted (F53-F56).
6. Market: a tiebreaker only (F57-F59).
Risks and unknowns
- Two TypeScript majors in the org. agent-workflow-tooling already uses TypeScript 7 (F27); the Vue repos stay on 6.0.x until vue-tsc and typescript-eslint support the 7.1 API. Neither the TypeScript 7.1 date nor the TypeScript 6 support window is known.
- npm 11 runs install scripts by default (F8). The protection depends on
.npmrcbeing present everywhere, including Docker build contexts. - Dependabot's pnpm v10 ceiling may simply be out-of-date documentation. Not tested.
- Lint experiment limits. One SFC on WSL2, with
npxstart-up time included. Biome and Oxlint change fast, so re-test in about six months. - Debugging distroless needs
:debugtags, and DHI would need a registry login in CI. - Org-admin settings. The SHA-pin policy and push protection are org settings, and the current tag pins must be updated before the policy is switched on.
- RQ-6 is a weak signal: a small, keyword-biased, US-only sample, leaning toward staffing firms.
- Agreed across spikes, which each ADR must keep consistent:
- storefront and admin both pick Pinia Colada;
- admin and checkout both pick nginx-unprivileged;
- every frontend needs the backend's cookie, CORS and CSRF design and its SDK generator (commerce-backend#1).
What this means for the plan
- A commerce ADR, "JavaScript runtime and package manager": Node LTS, npm hardened through
.npmrc, and the trigger for moving to npm 12. - The design-system ADR and packages:
- the four config packages, with peers
typescript ~6.0andeslint ^10; - published with trusted publishing; making a warning into an error counts as a major version;
- acceptance: a fixture SFC trips the template, floating-promise and
v-htmlrules, and vue-tsc fails on an undeclared template identifier; - open: an accessibility lint plugin. The storefront spike recommends
eslint-plugin-vuejs-accessibility.
- the four config packages, with peers
- Per-repo CI, as code lands:
- CodeQL languages;
- Dependabot ecosystems, groups and cooldown;
- dependency-review, hadolint, Grype and attest;
- actions re-pinned by SHA.
- Org settings task: the SHA-pin policy and push protection.
- A container ADR, in platform plus each app: the image table, Compose hardening and graceful shutdown.
- The backend-language ADR: the market signal breaks a tie only.
Verified by the lead before posting:
- endoflife.date's API: Node 26 becomes LTS on 2026-10-28 with EOL 2029-04-30, and Node 24 has EOL 2028-04-30;
- the Dependabot ecosystems page: npm v7–v11 and pnpm v7–v10 have security updates; Bun, Docker and Compose get none;
typescript-eslintpeerstypescript >=4.8.4 <6.1.0;typescriptis at 7.0.2;- vuejs/language-tools discussion #6121, "vue-tsc support for TypeScript 7";
- GHSA-69fq-xp46-6x23 (CVE-2026-33634, critical, "Trivy ecosystem supply chain temporarily compromised", 2026-03-21);
- SonarQube Cloud's JS/TS docs: "All versions up to 5.9.3 are supported";
- the experiment workspace, including its
ts7/project.
Evidence
RQ-1: Runtime and package manager
- F1 [sourced] Node 24's EOL is 2028-04-30. Node 26 becomes LTS on 2026-10-28, with EOL 2029-04-30. Node 22's EOL is 2027-04-30. (endoflife.date Node.js, accessed 2026-10-07)
- F2 [sourced] Vite "requires Node.js version 20.19+, 22.12+" (Vite guide, 8.3.1). Nuxt needs Node "22.x or newer (but we recommend the active LTS release)" (Nuxt installation, 4.x). Both accessed 2026-10-07.
- F3 [experiment] Engines: nuxt@4.6.0 needs
^22.22.3 || ^24.15.0 || >=26.0.0, and vite@8.3.3 needs^20.19.0 || >=22.12.0. Node 24.21.0 bundles npm 11.19.0, and Node 26.10.0 bundles npm 11.19.1. (npm view,nodejs.org/dist/index.json) - F4 [sourced] Type stripping is "Stable" in v25.2.0 and v24.12.0, for erasable syntax only, with no type checking. (Node.js TypeScript, accessed 2026-10-07, v26)
- F5 [sourced] Corepack ships with Node "from version 14.19.0 up to (but not including) 25.0.0". (nodejs/corepack, accessed 2026-10-07)
- F6 [sourced] Bun 1.4.2 came out on 2026-09-05, and Bun has no LTS (bun releases). Deno's LTS is "the Deno 2.9 line … maintained until January 31st, 2027" (Deno stability). Both accessed 2026-10-07.
- F7 [sourced] Dependabot supports npm v7–v11 and pnpm v7–v10, both with security updates. Bun (≥1.1.39), Docker and Compose get no security updates; Deno ≥v2 does. (Dependabot ecosystems, accessed 2026-10-07)
- F8 [experiment] Install scripts, tested with esbuild 0.25.0 (E3):
- npm 11.19 runs esbuild's postinstall and only warns. With
strict-allow-scripts=trueit fails withESTRICTALLOWSCRIPTS. - pnpm 12.9.1 fails with
ERR_PNPM_IGNORED_BUILDS. - Bun 1.4.2 runs it, because esbuild is on Bun's default trusted list.
- npm 11.19 runs esbuild's postinstall and only warns. With
- F9 [sourced] npm v12.0.0 (2026-07-08): "Dependency lifecycle scripts are now blocked by default";
allow-gitandallow-remotedefault tonone. (npm/cli v12.0.0, accessed 2026-10-07) - F10 [experiment] knip 6.40.0 was about 12 h old at test time (E4):
- pnpm 12 (defaults), npm with
--min-release-age=1and Bun withminimumReleaseAge=86400each resolved 6.39.0; - default npm and default Bun resolved 6.40.0;
- an exact pin bypassed pnpm's window.
- pnpm 12 (defaults), npm with
- F11 [sourced] "pnpm v10 disables automatic
postinstall", andminimumReleaseAge"defaults to1440" (pnpm supply-chain security). Bun "only runs lifecycle scripts for packages on an allow list" (Bun lifecycle). Both accessed 2026-10-07. - F12 [sourced] Trusted publishing "requires npm CLI version 11.5.1 or later", and npm generates provenance automatically on GitHub Actions. (npm trusted publishers, accessed 2026-10-07)
RQ-2: Toolchain
-
F13 [experiment] Twelve problems seeded into
ProductList.vue(E1):- script: P1 an unused variable, P2
any, P3letthat should beconst, P4 a floating.then, P5 a floating call, P6==, P7debugger; - template: T1 v-for without a key, T2 v-html, T3 v-if together with v-for, T4 a duplicate attribute, T5 an undeclared identifier.
Tool and config Caught Wall time ESLint 10.12.0 presets (typescript-eslint 8.71.1 recommendedTypeChecked, eslint-plugin-vue 10.11.1flat/recommended)9: P1 P2 P4 P5 P7 T1 T2 (as a warning) T3 T4 3.8–5.4 s ESLint plus prefer-const,eqeqeqandno-v-html: error11: everything except T5 — Biome 2.5.15, defaults 4 (P1 P2 P6 P7) plus 2 false positives (template-used variables reported as unused) 0.5–0.7 s Biome with full Vue support and the extra rules 7: P1 P2 P6 P7 T1 T3 T4, plus 2 accessibility findings 1.2–2.0 s Oxlint 1.87.0, defaults 1: P7 ~1.0 s Oxlint with the vue and typescript plugins and --type-aware3: P2 P6 P7 1.1–1.9 s - script: P1 an unused variable, P2
-
F14 [experiment] Biome's and Oxlint's floating-promise, unused-variable and prefer-const rules fire in
.tsfiles but not in the SFC. Oxlint's 46vue/*rules are all script-level, andvue/require-v-for-keyis "not found". eslint-plugin-vue loaded through Oxlint'sjsPluginsfails with "Use the latest vue-eslint-parser". (E1) -
F15 [sourced] Oxlint's JS plugins are "currently in alpha", and custom file formats such as Vue are unsupported (Oxlint JS plugins). Type-aware linting needs TypeScript 7.0+ (Oxlint type-aware). Both accessed 2026-10-07.
-
F16 [sourced] Biome's Vue support is experimental and needs
html.experimentalFullSupportEnabled. (Biome language support, accessed 2026-10-07, 2.x) -
F17 [sourced] eslint-plugin-vue "requires vue-eslint-parser" for
<template>, witheslint-config-prettierlast (eslint-plugin-vue guide).@vue/eslint-config-typescriptoffersrecommendedTypeChecked, and "Type-checking is much slower" (vuejs/eslint-config-typescript). Both accessed 2026-10-07. -
F18 [experiment] In create-vue 3.24.0's oxlint template,
lint:oxlintrunsoxlint . --fixand theneslint . --fix --cache. Oxlint is added to ESLint, not a replacement for it. (npm pack create-vue@3.24.0) -
F19 [inference] From F13-F18: only ESLint with eslint-plugin-vue and typescript-eslint covers template rules and type-aware rules inside SFCs.
-
F20 [experiment] Prettier 3.9.9, oxfmt 0.72.0 and Biome 2.5.15 (with
html.formatter.enabled) produced byte-identical output on a messy SFC. (E2) -
F21 [sourced] Prettier supports Vue out of the box (Prettier docs). oxfmt has been in beta since 2026-02-24 (Oxfmt Beta). Both accessed 2026-10-07.
-
F22 [sourced] TypeScript 7.0 (2026-07-08) is a native port with no API. "We expect TypeScript 7.1 to ship with a new (and different) API", and "Workflows that use Vue … will likely not yet be able to leverage TypeScript 7". (Announcing TypeScript 7.0, accessed 2026-10-07)
-
F23 [sourced] typescript-eslint supports TypeScript
>=4.8.4 <6.1.0. (dependency versions, accessed 2026-10-07, 8.x; also confirmed withnpm view typescript-eslint peerDependencies) -
F24 [experiment] typescript@7.0.2 exports only
./lib/version.cjsand./unstable/*. vue-tsc 3.3.12 on it fails withERR_PACKAGE_PATH_NOT_EXPORTED './lib/tsc', andtsc7 silently skips.vuefiles. (E1,ts7/) -
F25 [experiment] vue-tsc 3.3.12 on TypeScript 6.0.3 caught TS2339 (T5), TS1117 (T4) and the v-if/v-for scoping error (T3) in 9–11 s. (E1)
-
F26 [sourced] vue-tsc on TypeScript 7.0.2 fails with the
./lib/tscerror. (language-tools #6121, "vue-tsc support for TypeScript 7", accessed 2026-10-07) -
F27 [verified-code] agent-workflow-tooling already uses npm with a lockfile, Node ≥22, ESLint 10, Prettier 3 and TypeScript 7, with no Vue. (
agent-workflow-tooling/package.json:27-28,42,46,47@c1d006c) -
F28 [sourced] stylelint-config-standard-vue parses
.vuewith postcss-html, needs Stylelint ≥16, and goes last inextends. (stylelint-config-standard-vue, accessed 2026-10-07, 2.0.0) -
F29 [sourced] Vitest "requires Vite >=v6.4.0 and Node >=v22.12.0" and reuses
vite.config.*. (Vitest guide, accessed 2026-10-07, 5.0.3) -
F30 [sourced] Knip enables its compiler automatically in a Vue project, and finds unused files, unused dependencies and unlisted dependencies. (Knip compilers, accessed 2026-10-07, 6.x)
RQ-3: CI security
- F31 [verified-code] CodeQL runs only
[actions](.github/workflows/codeql.yml:24). Actions are pinned by tag at:27,:30and:36. Dependabot covers only github-actions (.github/dependabot.yml:4-8). git-secrets runs at.githooks/pre-commit:3-4. All inbackend@38a8548; the other repos are identical. - F32 [sourced] CodeQL supports
javascript-typescript(including.vue), Go, Rust and Actions (CodeQL languages). Rust became GA on 2025-10-14 (changelog). Both accessed 2026-10-07. - F33 [sourced] Dependabot's
cooldownapplies only to version updates, never to security updates.groupsandmulti-ecosystem-groupsexist. (Dependabot options, accessed 2026-10-07) - F34 [sourced] Secret scanning is free and automatic on public repos, but push protection "is disabled by default", and there is no Authorize.Net pattern. (secret scanning; push protection; patterns, accessed 2026-10-07)
- F35 [sourced] dependency-review-action blocks PRs that add vulnerable dependencies or disallowed licenses, and is free on public repos. Dependabot alerts cover existing vulnerabilities; dependency review stops new ones. (dependency-review-action v5.0.0; about dependency review, accessed 2026-10-07)
- F36 [sourced] OSV-Scanner reads npm, pnpm and bun lockfiles,
go.modandCargo.lock, and scans images (OSV-Scanner, v2.6.0). npm audit and pnpm audit query the registry's bulk advisory endpoint (npm audit; pnpm audit). All accessed 2026-10-07. - F37 [sourced] A full SHA "is currently the only way to use an action as an immutable release" (secure use). Repository and org policies have been able to require SHA pins since 2025-08-15 (changelog). Both accessed 2026-10-07.
- F38 [sourced] GHSA-69fq-xp46-6x23 / CVE-2026-33634 (critical), 2026-03-19 to 23: "Trivy ecosystem supply chain temporarily compromised". trivy v0.69.4, its images and setup-trivy were affected, and attackers "force-pushed 76 of 77 version tags" of trivy-action (Trivy advisory). A separate trivy-action script injection was fixed in 0.34.1 (GHSA-9p44-j4g5-cfx5). Both accessed 2026-10-07.
- F39 [sourced] Grype scans images and SBOMs using EPSS and KEV data (grype, v0.120.1), and
scan-actionemits SARIF (scan-action, v7.4.2). Docker Scout's free plan covers one repository (Docker pricing). hadolint lints Dockerfiles and runs ShellCheck on their commands (hadolint, v2.15.1). All accessed 2026-10-07. - F40 [sourced] zizmor's audits include
template-injection,excessive-permissions,unpinned-usesandartipacked(zizmor audits, v1.30.1). CodeQL's Actions queries cover code injection, unpinned tags, permissions and cache poisoning (CodeQL actions). Both accessed 2026-10-07. - F41 [sourced] Scorecard's checks include Pinned-Dependencies, Token-Permissions, Branch-Protection and Dangerous-Workflow. (Scorecard checks, v5.5.0, accessed 2026-10-07)
- F42 [sourced] GitHub artifact attestations are Sigstore-signed at SLSA v1.0 Build L2, free on public repos (artifact attestations). BuildKit adds
mode=minprovenance by default, and a Syft SBOM with--sbom=true(build attestations). Both accessed 2026-10-07. - F43 [inference] From F7 and F31-F42:
- Tag pins leave the repos open to the attack path used against Trivy (F38); the SHA policy closes it.
- Push protection covers contributors who never ran
.githooks/setup. - Dependabot's docker ecosystem has no security updates (F7), so only Grype sees base-image CVEs.
- Audit tools duplicate Dependabot plus dependency review on npm.
RQ-4: Container baseline
-
F44 [sourced]
node:24-slimis bookworm, a24-trixie-slimvariant exists, and Alpine "does use musl libc" (Docker Hub node; docker-node).golang:alpineis "not officially supported" (golang). All accessed 2026-10-07. -
F45 [sourced] "Node.js was not designed to run as PID 1"; use
--init, thenodeuser, andnodedirectly in CMD. (docker-node Best Practices, accessed 2026-10-07) -
F46 [sourced] Distroless now builds only on Debian 13: nodejs22, 24 and 26, plus static, base and cc, each with nonroot and debug tags and no shell. (distroless, accessed 2026-10-07)
-
F47 [sourced] Chainguard's free tier gets only
:latest; versioned tags are paid. (Chainguard lifecycle, accessed 2026-10-07) -
F48 [sourced] DHI has been free under Apache 2.0 since 2025-12-17, with SBOM, VEX and SLSA L3; the CVE-fix SLA is paid (DHI blog). Images are non-root and pulled from
dhi.ioafter a login (DHI docs). Both accessed 2026-10-07. -
F49 [sourced] Runtime behaviour:
- A shell-form ENTRYPOINT "does not pass signals" (Dockerfile reference).
--inituses tini (docker run).- Compose has
init,read_only,cap_dropandsecurity_opt(Compose services). - nginx-unprivileged listens on 8080 with temp files in
/tmp(nginx-unprivileged).
All accessed 2026-10-07.
-
F50 [experiment] Compressed amd64 sizes (E6):
- node:24-slim 80.8 MB; node:24-trixie-slim 82.5 MB; node:24-alpine 58.2 MB;
- distroless nodejs24 nonroot 55.3 MB (uid 65532);
- Chainguard node:latest 65.4 MB (Node 26.10.0);
- nginx-unprivileged:alpine-slim 8.27 MB;
- distroless static 0.86 MB.
None ships tini.
-
F51 [experiment] Distroless run with
--read-only --cap-drop ALL --security-opt no-new-privileges(E6):- the exec-form HEALTHCHECK reported healthy, and a write to
/appfailed withEROFS; - without
--init,docker stop -t 5took 6.87 s and exited 137 (killed); - with
--init, it took 0.53 s and exited 143.
- the exec-form HEALTHCHECK reported healthy, and a write to
-
F52 [inference] From F44-F51: distroless on trixie is the smallest free, non-root, shell-less Node runtime, and it shares glibc with the build stage. Chainguard's free
:latestisn't an LTS, DHI needs a login, and Alpine adds musl.
RQ-5: Quality gate
- F53 [sourced] SonarQube Cloud gives public OSI-licensed repos the Pro features for free (pricing). For TypeScript, "All versions up to 5.9.3 are supported" (JS/TS docs). Both accessed 2026-10-07.
- F54 [sourced] Codecov is free for public repos, with PR comments and patch coverage. (Codecov pricing, accessed 2026-10-07)
- F55 [sourced] Vitest's
coverage.thresholdscovers lines, functions, branches and statements, per file or per glob. (Vitest coverage, accessed 2026-10-07, 5.0.3) - F56 [inference] From F13, F25, F32 and F53-F55: ESLint, vue-tsc, Vitest thresholds and CodeQL already gate defects, types, regressions and security. Sonar duplicates them and supports neither TypeScript 6 nor 7.
RQ-6: Market signal (small sample)
-
F57 [experiment] 39 unique US postings mentioning Vue or Nuxt (Dice 18, Indeed 21), opened and coded on 2026-10-07 (E5):
Term Postings TypeScript 26 (67%) Node/Express/NestJS 22 (56%) Python 14 (36%) Docker 13 Java 12 (31%) C#/.NET 11 (28%) Nuxt 9 (23%) Go 3 (8%) PHP 3 Ruby 2 Rust 0 18 of the 39 list Vue only as one option alongside React or Angular. In the 21 where Vue is the team's stack, the backends are Node 9, Python 7, .NET 6, Java 4, Go 2 and Rust 0.
-
F58 [experiment] Mobile appears in 3 of 39 postings, always as "preferred"; Ionic in 1 and Capacitor in none. A Dice search for "Vue Ionic Capacitor" showed no Ionic or Capacitor titles in its top 10. (E5)
-
F59 [inference] From F57-F58: a Node/TS backend is the most common pairing with Vue, Go is a minority, and Rust, Ionic and Capacitor show no signal. Use this to break a tie only.
Current state
All seven child repos are docs-only, with the same CI scaffolding (F31): CodeQL on
actions, Dependabot forgithub-actions, and git-secrets hooks. The only code in the org isagent-workflow-tooling: npm, ESLint 10, Prettier 3 and TypeScript 7 (F27).Sources
Every source is cited inline in the findings above, with the date accessed (2026-10-07) and the version.
Experiments ran in a scratch directory on WSL2 with Node v24.21.0, npm 11.19.0 and Docker 29.8.0, with no repository changes:
- E1, lint and type checking. Installed vue 3.5.43, typescript 6.0.3, vue-tsc 3.3.12, eslint 10.12.0, typescript-eslint 8.71.1, eslint-plugin-vue 10.11.1, vue-eslint-parser 10.4.1, @biomejs/biome 2.5.15, oxlint 1.87.0 and oxlint-tsgolint 7.0.2003.
- Commands:
npx eslint src(with presets, theneslint.tuned.config.js);npx biome lint src(with defaults, thenbiome.full.json);npx oxlint src(defaults,--type-aware, thenoxlint.jsplugins.json);npx vue-tsc --noEmit. Ints7/,npx vue-tsc --noEmitandnpx tsc --noEmit --listFilesOnly. - Each was timed three times with
/usr/bin/time.
- Commands:
- E2, formatting.
prettier --write,oxfmtandbiome format, thendiff; the outputs were identical. Also inspected create-vue 3.24.0's oxlint template. - E3, install scripts. esbuild 0.25.0, installed with npm (with and without
strict-allow-scripts), pnpm 12.9.1 and Bun 1.4.2. - E4, release age. knip 6.40.0, published 2026-10-06T16:07:13Z and tested at 2026-10-07T04:16Z.
- E5, job boards, read-only.
- Dice: "Vue Nuxt TypeScript", "senior Vue developer", "full stack Vue Node", "Nuxt developer", "Vue.js Golang", "Vue.js" (remote) and "Vue Ionic Capacitor".
- Indeed: "Vue.js developer", "Vue Nuxt", "Nuxt.js", "full stack Vue Node TypeScript" and "Vue Ionic".
- ZipRecruiter returned titles only, so it isn't counted.
- 44 detail calls gave 39 unique postings. Posting text was treated as data only.
- E6, containers.
docker pullof each base image, with sizes fromdocker image ls --tree. A distroless probe withHEALTHCHECK CMD ["/nodejs/bin/node","/app/healthcheck.mjs"], run withdocker run -d [--init] --read-only --cap-drop ALL --security-opt no-new-privilegesthendocker stop -t 5. The probe containers and pulled images were removed.
workflow · claude-code · 2026-10-07 00:41
- added a sub-issue
on Oct 7, 2026 - addedstage:researchBeing researchedBeing researchedstage:doneDoneDoneand removedstage:doneDoneDonestage:researchBeing researchedBeing researched
on Oct 7, 2026 - added a sub-issue
on Oct 7, 2026 - addedstage:researchBeing researchedBeing researchedstage:doneDoneDoneand removedstage:doneDoneDonestage:researchBeing researchedBeing researched
on Oct 7, 2026
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Choose the language, framework, toolchain, quality and security checks, and container baseline for each application, before any code lands.
Status
Appetite: 3h active (Default) · Estimate: — · Active so far: see the
timecommentBrief
Idea: "A research spike separately for admin, backend, checkout and storefront to determine the best tech stacks for each. I prefer TypeScript and Vue in some form (Vue, Nuxt, Ionic etc.) for frontend apps. I am open on the backend: if the best is TypeScript, is Node, Bun or Deno better? Should we do it in Rust or Go? We are not just looking for languages and frameworks though: what about linters, code quality and security checks. Each service will be in a Docker container."
Problem and audience: No repository has code yet, and none has a stack. Contributors, and the agents that scaffold each repo, need one researched choice per application. The choice covers the language, runtime and framework, plus the lint, format, type-check, test, quality and security tooling, and the container it ships in. The platform is also a portfolio, so a hiring manager reading the repos is a second audience.
Outcome: Each spike ends with a recorded decision. This epic ends with one table of stacks that agree where they meet: the generated SDK, the shared frontend tooling the design system publishes, the security checks, and the container baseline.
Scope.
Constraints:
AGENTS.md:29).AGENTS.md:12-13).design-system/README.md:16).actionsonly (backend/.github/workflows/codeql.yml:24), Dependabot for actions only (backend/.github/dependabot.yml:8), and git-secrets in the commit hooks (backend/.githooks/pre-commit:3). The other repos are identical.README.md:59).Known in code: No code exists in any repo.
backend/.github/workflows/codeql.yml:24andbackend/.github/dependabot.yml:8both wait for "this repository's languages / package ecosystem as its code lands". The stacks chosen here fill them in.Track: full — the answers need sources outside the repository, and options to compare · Appetite: 3h active for the shared baseline (Default)
Assumptions:
design-system/AGENTS.md:8-10).Spec
Not written yet.
Plan
Not written yet.
Decisions
Package manager: the bundled npm, hardened in
.npmrcwithstrict-allow-scripts,min-release-age=7,allow-git=none,allow-remote=noneandengine-strict, and installed withnpm ci.Move to npm 12 once Dependabot lists it. pnpm 10 is the runner-up; Bun and Deno are rejected.
• ESLint 10 with typescript-eslint
recommendedTypeCheckedand eslint-plugin-vue;• Prettier 3;
• vue-tsc 3 on TypeScript
~6.0until the Vue tools support TS 7.1;• Stylelint with
standard-vue;• Vitest 5 with coverage thresholds;
• Knip.
• CodeQL for
javascript-typescriptandgo;• an org policy requiring actions pinned by SHA;
• Dependabot for npm, docker, compose and gomod, with groups and a 7-day cooldown;
• push protection, plus an Authorize.Net pattern in git-secrets;
• dependency-review-action;
• hadolint;
• Grype;
•
buildx --sbomplusactions/attest.Reject: audit and OSV-Scanner, Trivy, Docker Scout, Scorecard and zizmor as CI jobs, syft and cosign.
nonroot: nodejs for the storefront, static for Go. Static SPAs run onnginx-unprivileged:alpine-slim, pinned by digest. Every service runs as a numeric non-root user, with an exec-form CMD, a HEALTHCHECK,init: true, a read-only root plus a/tmptmpfs,cap_drop: ALLandno-new-privileges.• Customer: a host-only
__Host-cookie onapi.,SameSite=Lax. The storefront server holds no session.• Staff: a separate
__Host-cookie name,SameSite=Strict, with an admin-only CORS allowlist and Origin checks on admin endpoints.• Both: HttpOnly, Secure,
Path=/. CSRF defence is an Origin/Sec-Fetch-Sitecheck plus a custom header on unsafe methods, because SameSite doesn't separate the same-site*.rsl-commerce.testhosts.The backend ADR specifies the details.
npm.pkg.github.com), not the public npm registry, because this isn't a public library.• Scope:
@reference-systems-lab, the org's own namespace on GitHub. Packages:/tokens,/ui-core,/ui,/eslint-config,/prettier-config,/stylelint-config,/tsconfig. Read the@rsl/*names in the research comments as@reference-systems-lab/*.• Consumers (storefront, admin, checkout, platform docs):
◦ map the scope in
.npmrcwith@reference-systems-lab:registry=https://npm.pkg.github.com;◦ get read access through each package's "Manage Actions access", so CI and Dependabot authenticate with
GITHUB_TOKEN;◦ pass the token to Docker builds as a BuildKit secret, never as a layer or build arg;
◦ for developers, use a classic PAT with
read:packagesin~/.npmrc.• Unchanged: semver ranges and DE-2's
allow-remote=none.• Not affected: the platform's default pinned images need no token; only
LOCALbuilds of frontends do.docs.rsl-commerce.testserves the engineering docs from the platform repo, and a newdesign.rsl-commerce.testserves the design-system docs. That makes eight local hosts.Stacks chosen
distroless/staticuseAsyncData+ Pinia Colada; nuxt-security with a nonce-based strict CSP; Unhead, sitemap, robots and schema-org; axe and Lighthouse CI; VueUseuseWebSocket;useAnalytics(); no PWA; distroless nodejs'self'with Trusted Types and SRI; nginx-unprivileged/me; VueUseuseWebSocket; Vitest 5 (Node + Browser Mode), MSW 3 and Playwright; nginx-unprivilegedui-core(no dependencies) andui(Reka UI); plain CSS in@layerwith one file per component, and strict-CSP authoring rules; tsdown, Changesets, private GitHub Packages publishing; VitePress 2 docs with a/playgroundpage; no separate workshop; Vitest Browser Mode, axe, screenshots in a pinned Playwright image, size-limitcompose.platform.yamlcontracts and digest-pinned GHCR images (opt-in local builds); Make over POSIX scripts; an openssl name-constrained local CA trusted through OS tools; nginx-unprivileged proxy; PostgreSQL 18, Valkey 9.1, RabbitMQ 4.3, Mailpit, Meilisearch (hardened); otel-lgtm; Playwright E2E with the backend's fake gateway, Schemathesis, Dependabot-updated matrix; VitePress 2 engineering docs atdocs.with Swagger UI and pre-rendered MermaidADRs to write, through PRs in the repos they affect:
Revisions
gh attestation verifyfrom itsdist.tarballURL with the read token, checked againstdist.integrity.npm pack <spec>fails withEALLOWREMOTEunder DE-2'sallow-remote=none. Found in Backend skeleton: health, one catalog endpoint, OpenAPI and image commerce-backend#2 (S10); corrected in design-system#1 DS-D8. No decision changes.@eslint/css, configured in@reference-systems-lab/eslint-config. Every Stylelint release depends onbraces, which has an unfixed high-severity advisory (GHSA-vfj7-8cjw-p6xm), and the system allows no high-severity exceptions (@ABilenduke). CSS moves into.cssfiles, because@eslint/csscan't read.vue<style>blocks. See Design-system skeleton: tokens and config packages on GitHub Packages design-system#2 D-9 and D-10.min-release-age-exclude[]=@reference-systems-lab/*, because DE-2's seven-day window blocked our own fresh releases (Exempt our own scope from the npm release-age window #10). The backend SDK,commerce-api, stays private.Links