Skip to content

chore: post-ExtJS consistency — remove leftovers, migrate stragglers, describe the present#490

Merged
CybotTM merged 25 commits into
mainfrom
chore/post-extjs-consistency
Jul 2, 2026
Merged

chore: post-ExtJS consistency — remove leftovers, migrate stragglers, describe the present#490
CybotTM merged 25 commits into
mainfrom
chore/post-extjs-consistency

Conversation

@CybotTM

@CybotTM CybotTM commented Jul 2, 2026

Copy link
Copy Markdown
Member

Description

Post-ExtJS consistency pass: after the SolidJS/Vite migration, docs, tool config, templates and dependency wiring still described or served the removed stack. Every change either removes something verified unreferenced, migrates something to its proper home, or rewrites a description to state the current contract. A five-scanner dependency/orphan audit (adversarially verified) contributed the second half of the commits.

Type of Change

  • Documentation update
  • Code refactoring
  • Bug fix (non-breaking change that fixes an issue)

Related Issue

N/A — follow-up to #489. Logout CSRF lands separately on top of this branch.

Changes Made

Consistency pass

  • Stale stack references removed everywhere (README, copilot-instructions, CodeQL/Sonar exclusions, tool configs); comments describe the present only.
  • header.css folded into the Vite build (un-minified 1:1, cascade verified in the emitted bundle); logo migrated to public/images/; public/build/ no longer exists.
  • Active-nav state derived client-side only — also fixes the mobile drawer permanently highlighting "Overview" (new nav.test.ts).
  • Dead Alt+D/E Help-page shortcuts removed; 403 page restyled in the app's design language with light-dark() + plain-color fallbacks; Node aligned to 26; E2E_TEST_PLAN.md rewritten.

Audit fixes (each independently re-verified)

  • Silently broken plumbing: SLSA provenance job never exported its outputs (Level-3 generator never ran); default_table_options.collate ignored by DBAL 4 (→ collation); dev DSN claimed MySQL 8 against MariaDB 12.1 (wrong DDL platform) — serverVersion unified on mariadb-12.1.2; make e2e-up/validate-stack/analyze-coverage/npm targets repaired.
  • Manifest truth: ext-pdo_mysql/ext-apcu/ext-intl declared; symfony/expression-language, dg/bypass-finals, symfony/mailer, symfony/form, symfony/css-selector, phpdocumentor/reflection-docblock removed (zero usage each, 11 further lock transitives dropped); symfony/stopwatch → require-dev; dead npm overrides removed.
  • Docker: APCu pinned via pecl 5.1.28 (was an unpinned git-master build); redundant xml extension build + composer COPYs + chromium apt list removed; buildx InvalidDefaultArgInFrom lint declared intentional (bake is the version source of truth).
  • Config/orphans: dead http_discovery.yaml (uninstalled package), twig_extensions.yaml (no-op), routes/dev/framework.yaml (double-loaded routes), sentry.yaml.dist (never loaded), unused cache pools (docs rewritten), SERVICE_USERS wiring + false docs claim (switch_user documented instead), db-test compose service, orphaned ControllingController, dev-users.ldif, activities translation catalogs; validators.de.yml added so the team-requirement violation actually translates.
  • Docs: all 17 broken internal links repaired (re-verified: 0 remain); techstack.md rewritten to the real stack; api.md documents the actual /scripts/timeSummaryForJira response; example code references real class names; commands run in images that contain the tools (app-dev/app-tools).

Testing

  • Unit tests green locally (1557); frontend 346 (incl. new nav.test.ts); full suite in CI
  • phpstan level 10 [OK], cs-fixer 0 findings, phpat via hooks
  • lint:container green in dev/prod/test after every config change; compose config valid; composer validate clean
  • Vite bundle verified: chrome defaults precede overrides; no removed rules
  • E2E / docker bake — CI

Code Quality

  • Code follows project coding standards
  • Self-review completed
  • Documentation updated
  • No breaking changes

Migration Notes

APP_LOGO_URL overrides keep working; default logo now at /images/logo-netresearch.svg. Node tooling expects 26. Local .env gains an APP_ENCRYPTION_KEY doc block; MAILER_DSN/SERVICE_USERS/messenger blocks are gone (nothing read them).

Checklist

Copilot AI review requested due to automatic review settings July 2, 2026 00:20

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request cleans up the codebase by removing all remaining references, comments, and configurations related to the legacy ExtJS frontend, finalizing the transition to the SolidJS UI. Key changes include updating documentation, Docker configurations, dependency manifests, and E2E tests, as well as refactoring the CSV export template. Feedback highlights an issue in the CSV export template where looping over external labels in the row data can lead to a variable number of columns and malformed CSVs, alongside a potential Twig runtime exception if the labels are null.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread templates/export.csv.twig
@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.14%. Comparing base (fccda9c) to head (bdd2d4e).

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #490      +/-   ##
============================================
+ Coverage     83.99%   84.14%   +0.15%     
+ Complexity     2816     2814       -2     
============================================
  Files           188      187       -1     
  Lines          7603     7588      -15     
============================================
- Hits           6386     6385       -1     
+ Misses         1217     1203      -14     
Flag Coverage Δ
integration 53.13% <ø> (+0.09%) ⬆️
unit 49.51% <ø> (+0.09%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR performs a post-ExtJS cleanup/consistency pass after the SolidJS/Vite migration: removing stale stack references and leftover artifacts, moving remaining “build output” into proper source locations, and updating docs/config to reflect the current runtime/tooling contract.

Changes:

  • Fold the previously-committed public/build header CSS into the Vite/Solid frontend source tree, and remove remaining public/build references (including CI artifacts).
  • Remove server-side nav “active” state (previously hardcoded) and rely on client-side route-based syncing; add coverage for drawer/icon/nav active state.
  • Update configuration/docs/dependency wiring to reflect the current stack (logo location, Node version, remove http_discovery, refresh e2e documentation).

Reviewed changes

Copilot reviewed 62 out of 65 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
templates/ui/index.html.twig Drops legacy header.css asset; stops passing hardcoded active
templates/partials/theme-init.html.twig Updates theming description for current shells/pages
templates/partials/header.html.twig Removes server-side active markup; relies on data-nav syncing
templates/partials/header-behavior.html.twig Updates header-behavior docs to current chrome-only usage
templates/partials/density-init.html.twig Updates density docs to current Solid shell contract
templates/export.csv.twig Removes dead labels header loop from CSV header
templates/bundles/TwigBundle/Exception/error403.html.twig Restyles 403 page and applies saved theme pre-paint
symfony.lock Removes stale Flex recipe entries (discovery, messenger)
src/Repository/EntryRepository.php Updates comments to remove ExtJS terminology
src/EventSubscriber/ExceptionSubscriber.php Updates comment describing JSON-error routing
src/Dto/InterpretationFiltersDto.php Updates comment for start pagination meaning
src/Dto/EntrySaveDto.php Updates comments to be client-agnostic (no ExtJS mention)
src/Controller/Tracking/SaveEntryAction.php Updates comments describing save semantics
src/Controller/Default/IndexAction.php Updates docs for root redirect into Solid SPA
src/Controller/Default/GetDataAction.php Updates docs for row-wrapped response shape
src/Controller/Default/ExportCsvAction.php Stops passing unused labels template variable
README.md Updates stack description and Node requirement
public/images/logo-netresearch.svg Adds/moves default logo into public/images/
public/build/css/header.css Removes committed Encore-era header CSS artifact
package.json Tightens Node engine requirement for root tooling
frontend/vite.config.ts Updates build-output comment to current directory structure
frontend/src/styles/header.css Adds unminified header chrome CSS to Vite sources
frontend/src/styles/app.css Imports header defaults before compaction overrides
frontend/src/pages/Tracking.tsx Removes ExtJS references in comments/docs
frontend/src/pages/Tracking.test.tsx Aligns test comments with current (non-ExtJS) behavior
frontend/src/nav.ts Extends route-based active syncing to mobile drawer links
frontend/src/nav.test.ts Adds tests covering active-state syncing across header variants
frontend/src/lib/timeParse.ts Updates docs for date/row formats (non-ExtJS wording)
frontend/src/lib/shortcuts.ts Drops obsolete Alt+D/E shortcuts and updates descriptions
frontend/src/lib/dateFormat.ts Updates token-table comment wording
frontend/src/header.ts Updates docs/comments to current SPA header behavior
frontend/src/header.test.ts Updates test description wording
frontend/src/App.tsx Updates theming comment to current ownership model
frontend/src/api/queries.ts Updates docs for row-wrapped API response shapes
frontend/README.md Updates frontend docs to current stack and build output path
frontend/messages/en.json Removes unused shortcut strings
frontend/messages/de.json Removes unused shortcut strings
e2e/worklog.spec.ts Updates E2E description wording to current UI
e2e/settings.spec.ts Updates E2E description wording; removes ExtJS references
e2e/navigation.spec.ts Updates E2E description and removes ExtJS tab-bar assertions
e2e/interpretation.spec.ts Updates E2E description wording
e2e/helpers/navigation.ts Updates helper docs to current navigation model
e2e/helpers/grid.ts Updates helper docs to current worklog grid model
e2e/helpers/date.ts Removes outdated ExtJS reference from helper docs
e2e/helpers/auth.ts Updates helper docs for current post-login landing
e2e/export.spec.ts Updates E2E description wording for export flow
e2e/E2E_TEST_PLAN.md Rewrites plan into an accurate suite map and current gaps
e2e/admin/admin-ui.spec.ts Updates E2E description wording
e2e/admin-inline-edit.spec.ts Updates E2E description wording
Dockerfile Updates build comments to current Vite/Solid-only asset build
docker-bake.hcl Aligns Node version default to 26
config/services.yaml Updates default logo URL path to /images/...
config/reference.php Regenerates reference config types to remove Encore config
config/packages/twig.yaml Removes unused bootstrap form theme configuration
config/packages/pentatrion_vite.yaml Updates comments to reflect current single-frontend reality
config/packages/http_discovery.yaml Removes unused PSR-17 discovery wiring
composer.json Removes unused allow-plugin entry
.sonarcloud.properties Updates analysis exclusions after old trees removal
.nvmrc Aligns local Node version to 26
.github/workflows/ci.yml Removes public/build prep/artifacts; keeps Vite build step
.github/dependabot.yml Updates root npm lockfile description to current usage
.github/copilot-instructions.md Updates repo instructions to current SolidJS/Vite stack
.github/codeql/codeql-config.yml Removes ignore entries for deleted ExtJS/build trees
.env Updates default APP_LOGO_URL to new images path

Comment thread templates/partials/theme-init.html.twig Outdated
Comment thread templates/bundles/TwigBundle/Exception/error403.html.twig
Comment thread templates/bundles/TwigBundle/Exception/error403.html.twig
Comment thread templates/bundles/TwigBundle/Exception/error403.html.twig
CybotTM added 10 commits July 2, 2026 02:44
The ExtJS shell and its Encore build are gone; align docs and tool
config with the SolidJS/Vite reality:

- README.md: tech stack now lists SolidJS, TypeScript, Vite, Tailwind
  CSS instead of the removed ExtJS/Stimulus/SCSS/Encore stack.
- CodeQL config: remove paths-ignore entries for the deleted
  assets/js/ext-js and public/build/js/ext-js trees.
- SonarCloud: drop the assets/js/** exclusion (tree deleted) and the
  ExtJS rationale from the scope comment.
- ci.yml / Dockerfile / pentatrion_vite.yaml: update comments that
  described the Encore build as still being retired.
- twig.yaml: remove the unused bootstrap_4_layout form theme (no Twig
  form rendering exists; the login form is hand-written HTML).
- config/reference.php: regenerate — drops the stale WebpackEncoreConfig
  shape and picks up the when@profiling env block.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
public/build/css/header.css was a frozen, minified Encore artifact with
no source in the tree (committed static in 4112bc2) and the last live
consumer of the legacy public/build CSS path. Header styling was split
across two build systems: this file loaded first via a plain <link>,
then overridden by app.css rules from the Vite bundle.

Move it into the Vite build as frontend/src/styles/header.css:

- un-minified 1:1 (re-minified output verified byte-identical), except
  the dropped '#header,#header-body{overflow:visible!important}' rule —
  those element IDs were the ExtJS viewport regions and match nothing
  in the SPA. The #nrnavi corporate-nav iframe rule is kept.
- imported at the top of app.css so the compaction/sidebar overrides
  keep winning the cascade (verified in the emitted bundle: chrome
  defaults precede the ultra-compact overrides).
- drop the legacy <link> from ui/index.html.twig; Vite emits the CSS
  via vite_entry_link_tags('app').

public/build now only holds the committed logo images.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
The header partials and their hydration code still described themselves
as 'framework-neutral, shared by BOTH shells (ExtJS and SolidJS)'. The
ExtJS shell is gone; reword the docblocks to describe the current
architecture (server-rendered chrome that paints before the Vite bundle
loads, hydrated by header.ts/nav.ts), aligned with font-init's wording.

Comment-only: header.html.twig, header-behavior.html.twig,
theme-init.html.twig, density-init.html.twig, App.tsx, header.ts,
nav.ts. The re-include idempotency guard keeps its (now re-include-only)
justification.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
The Help page and shortcuts dialog still listed Alt+D (delete) and
Alt+E (edit) in the worklog accelerators. Those were 'classic-only'
shortcuts of the removed ExtJS grid — Tracking.onGridShortcut handles
only Alt+C/P/R/X/I (plus Alt+A via the header add shortcut), so the
two entries documented shortcuts that no longer work anywhere. Drop
them and their now-unused help_sc_delete/help_sc_edit messages (en+de).

Also correct remaining comments that described ExtJS as still running
(App.tsx theming note, header.ts shortcut docblock, Tracking.tsx grid
docblock).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
The header's server-side 'active' parameter was hardcoded to 'month'
(ui/index.html.twig) and overwritten by nav.ts syncNav() within a tick
of hydration — on any non-month route it briefly marked the WRONG item
(including a wrong aria-current for screen readers). Drop the parameter
and all its template conditionals; the route is the single source of
truth.

This also fixes a real bug it was masking: syncNav() never updated the
mobile drawer links, so the drawer permanently highlighted 'Overview'
regardless of route. Include .drawer-link[data-nav] in the sync and add
nav.test.ts covering bar, icon and drawer links (346 frontend tests
green).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
public/build existed only to serve two images from the retired asset
pipeline's output path. Give the logo a proper static home instead of
explaining the old path in comments:

- move logo-netresearch.svg to public/images/ and point the
  app_logo_url default (services.yaml) and APP_LOGO_URL (.env) at it
- delete logo.png (referenced by nothing)
- drop every remaining public/build reference: CodeQL + SonarCloud
  exclusions, the CI prepare/artifact steps, and the describing
  comments in ci.yml, Dockerfile, pentatrion_vite.yaml and header.css
- copilot-instructions.md: the frontend section still described the
  Encore pipeline; describe the SolidJS/Vite build instead
- frontend/README.md: correct the output path (public/build-ui)

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Comments and test names across the frontend and e2e suite still narrated
the ExtJS era ('the ExtJS shell was removed', 'migrated out of the ExtJS
shell', 'like the ExtJS header', 'shared with the ExtJS shell'). Rewrite
each to state the current contract instead: the wire format (row-wrapped
[{<rowKey>: {...}}]), the endpoint semantics (/tracking/delete reads
form params; /getData rows carry d/m/Y), and the current UI (worklog
grid at /ui/tracking).

Also drop navigation.spec.ts's assertion that no '.x-tab-bar' exists —
it guarded the removal of a component that can no longer reappear — and
the doc reference to a nonexistent keyboard.spec.ts.

The one remaining ExtJS mention (security.yaml logout comment) is
addressed by the logout-CSRF change.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- config/packages/http_discovery.yaml wired Http\Discovery\Psr17Factory
  and six PSR-17 aliases, but php-http/discovery is not installed and
  nothing type-hints those interfaces (the Jira integration uses Guzzle,
  whose OAuth1 middleware ties it to Guzzle anyway) — the container only
  compiled because the unreferenced services were pruned. Remove the
  file, the stale allow-plugins entry and the orphaned symfony.lock
  recipe, plus the symfony/messenger recipe (package equally absent).
- Node: one version everywhere — .nvmrc, docker-bake NODE_VERSION and
  the root engines field now all say 26 (nodesource setup_26.x
  verified); README described Node as the asset compiler, but assets
  build with bun — Node serves the Playwright e2e tooling.
- e2e/E2E_TEST_PLAN.md: rewritten to describe the actual suite (14
  specs + 8 helpers); it still listed spec files that no longer exist
  and proposed a structure that was never adopted.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
error403.html.twig still carried its original blue/gray panel styling
(tahoma font stack, #15428b headers, gradient panel chrome) and ignored
the saved light/dark preference. Restyle with the app's light-dark()
palette and 44px targets, and include partials/theme-init.html.twig so
the page follows the user's theme like login and the SPA shell do
(theme-init no-ops its button wiring when no #theme-cycle exists).

Also drop the never-emitting labels header loop from export.csv.twig —
ExportCsvAction always passed labels=null, and the functional test
already asserts a header without those columns — and stop passing the
variable.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
The buildx lint rule InvalidDefaultArgInFrom flagged PHP_BASE_IMAGE and
COMPOSER_IMAGE on every CI build (6 annotations per run, also on main).
The default-less ARGs are a documented design choice — docker-bake.hcl
is the single source of truth for versions — so skip that rule via the
check directive instead of duplicating the pins.

Also fix the README manual-install block: it still called for Node 22
and 'npm run build' (no such root script); the frontend builds with bun.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@CybotTM CybotTM force-pushed the chore/post-extjs-consistency branch from d6b6b69 to 1fb9844 Compare July 2, 2026 00:45
CybotTM added 10 commits July 2, 2026 02:51
Verified against the dependency graph and the runtime (audit findings,
each independently re-verified):

- require ext-pdo_mysql: the app connects via mysql:// DSNs and nothing
  else provides the extension (not bundled in php:8.5-fpm; no lock
  package requires it) — the manifest understated a hard dependency.
- drop symfony/expression-language: no '@=' expressions, no route
  conditions, no security expressions anywhere; container lint verified
  green in dev/prod/test without it.
- drop dg/bypass-finals (dev): BypassFinals::enable() is called nowhere
  (no bootstrap call, no phpunit extension block) — inert since install.
- move symfony/stopwatch to require-dev: only the dev-context
  performance suite uses it (stays in the lock's prod section via
  doctrine/migrations).
- drop the 'security-check' script: it curl-piped a third-party phar at
  a pinned 2-major-old version; composer audit already gates via
  captainhook and make audit.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
serialize-javascript, uuid and http-proxy-middleware targeted advisories
in the retired build toolchain's dependency tree. The root manifest now
holds only @playwright/test + @axe-core/playwright, and none of the three
packages exists anywhere in package-lock.json — the overrides matched
nothing. Verified: npm install --package-lock-only leaves the resolved
tree untouched (the only lock delta is the engines field catching up
with the Node-26 pin).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- docker-php-ext-install xml recompiled an extension the php:8.5-fpm
  base image already ships statically (verified by running the bare
  image: xml/dom/simplexml/libxml all loaded), and libxml2-dev existed
  only for that build. If a future base ever dropped ext-xml, composer
  would fail loudly (symfony/framework-bundle requires it).
- tools/dev/profiling repeated the composer COPY they already inherit
  from the deps stage.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Audit findings, each independently re-verified:

- doctrine.yaml: default_table_options used 'collate' — DBAL 4 reads
  only 'collation' (AbstractMySQLPlatform), so the option was silently
  ignored. Rename the key.
- compose.yml app-e2e DSN: serverVersion mariadb-12.1.0 → 12.1.2,
  matching .env.test and the mariadb:12.1 images.
- config/routes/dev/framework.yaml only re-imported routes.yaml, which
  MicroKernelTrait already loads unconditionally — the dev routes were
  registered twice. Delete.
- config/packages/twig_extensions.yaml registered nothing (an empty
  _defaults block plus commented-out lines for the abandoned
  twig/extensions package). Delete.
- .env: drop the symfony/messenger recipe block (package removed);
  document APP_ENCRYPTION_KEY in .env and .env.example (referenced by
  services.yaml and four docs, previously undocumented in env files).
- .env.test: drop APP_RUNTIME_ENV (symfony/runtime is not installed;
  nothing reads it).
- phpat.php: drop the Sensio namespace selector
  (sensio/framework-extra-bundle is long gone).
- routes.yaml: comments referenced Symfony 7.3/6.4 conventions on a
  Symfony 8.1 app and announced a login_check removal that never came —
  state the actual reason it stays.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- src/Controller/ControllingController.php: a routeless shim of
  protected static spreadsheet helpers with no subclass and no caller
  (routes live in Controlling\ExportAction; the web test never imports
  the shim).
- translations/activities.{de,en}.yml: the 'activities' domain was
  consumed only by the NrArrayTranslator Twig filter removed in #489;
  every remaining trans call uses the default or security domain.
- docker/ldap/dev-users.ldif: compose mounts only users-only.ldif;
  the file was referenced by three comments/docs, now repointed.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Audit findings, each verified against the real Makefile, composer
scripts, compose services and installed packages:

- Makefile: validate-stack and analyze-coverage ran in the 'app'
  service, whose production image ships neither composer nor dev tools
  — point them at app-tools/app-dev.
- Commit .env.test.local.example: 'make e2e-up' tried to cp it since
  forever; the file never existed, so the target always fell back to a
  brittle echo with literal \n escapes.
- copilot-instructions.md: dropped Psalm (never installed) and the
  security-check script; PHPUnit 12→13; test/quality commands moved to
  app-dev/app-tools; frontend section now bun/Vite; removed references
  to nonexistent PLANNING.md, docs/security-checklist.md and assets/.
- tests/AGENTS.md: no ParaTest, no composer test:parallel, test db is
  db_unittest, PHPUnit 13.
- tests/README.md: real filter target (testSaveAndDeleteWorkLog), real
  analyze-coverage path/service.
- docs/development.md: bun frontend workflow, real db targets
  (reset-test-db), nvm use 26.
- docs/TROUBLESHOOTING.md: asset rebuild via bun/Vite instead of the
  removed npm/webpack pipeline.
- docs/testing.md: composer audit instead of the removed
  security-check script.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- slsa-provenance.yml: the provenance job never declared job-level
  outputs, so needs.provenance.outputs.has_subjects was always empty
  and the SLSA Level 3 generator job never ran on any release. Map the
  step outputs.
- Makefile npm-build/dev/watch invoked root 'npm run build|dev|watch' —
  scripts that do not exist (root npm is Playwright-only). Run the real
  bun/Vite commands in frontend/.
- serverVersion truth: the dev DSN claimed 'mysql 8' and doctrine.yaml
  '10.11' while every compose database runs mariadb:12.1 — Doctrine
  picked the wrong DDL platform (DSN wins over yaml). Align .env,
  doctrine.yaml and the e2e template on mariadb-12.1.2.
- e2e-up: use the now-committed .env.test.local.example instead of the
  echo fallback whose \n escapes were written literally.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Audit-verified (zero consumers each), removed atomically with their
config/env/doc surfaces:

- symfony/mailer + mailer.yaml + MAILER_DSN (.env, deployment guide):
  no code path sends mail.
- symfony/form + csrf.yaml: no Twig forms exist (login is hand-written
  HTML); login/logout CSRF runs on security-csrf + the stateless
  framework.csrf_protection config, independent of the Form component.
- dev-deps symfony/css-selector + phpdocumentor/reflection-docblock:
  no crawler filtering, no docblock parsing (property_info uses
  phpstan/phpdoc-parser). 11 lock packages drop with the transitives.
- SERVICE_USERS parameter + env + the docs/features.md impersonation
  claim: the parameter had zero consumers while the docs advertised it
  as a feature; the working impersonation path is Symfony switch_user
  (simulateUserId), now documented instead.
- cache pools (cache.auth/config/data/session/debug): defined and
  per-env tuned, but nothing ever injected them — only cache.app is
  used. Kept prefix_seed + the APCu app adapter; docs/apcu-setup.md
  rewritten to match.
- sentry.yaml.dist: Symfony never loads .dist files; the bundle runs on
  its defaults.
- compose db-test service + volume + app-dev depends_on: tests use
  db_unittest; nothing pointed at db-test.
- translations: drop messages.ru.yml (one key, for a locale the UI does
  not offer); add validators.de.yml re-keying the team-requirement
  violation to the exact constraint message and domain the validator
  actually translates.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
- APCu was compiled from an unpinned git clone of krakjoe/apcu master —
  every build could ship a different arbitrary commit, contradicting the
  Dockerfile's own single-source-of-truth versioning. pecl apcu-5.1.28
  builds and loads cleanly on php:8.5-fpm (verified); pin it in
  docker-bake.hcl like Xdebug and mirror the ARG.
- Declare ext-apcu and ext-intl in composer.json: the app cache is
  bound to cache.adapter.apcu and intl is deliberately installed for
  native ICU behavior — the manifest now states both (the mailer/form
  removal also dropped symfony/polyfill-intl-icu, making native intl
  the only provider).
- e2e stage: drop the hand-maintained 17-package chromium apt list;
  'npx playwright install chromium --with-deps' already installs
  Playwright's canonical dependency set.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Audit-verified doc debt:

- Repair all 17 broken relative links that pointed at seven never-
  tracked files (DEVELOPER_SETUP, PROJECT_INDEX, API_USAGE_GUIDE,
  DEVELOPER_ONBOARDING_GUIDE, API_DOCUMENTATION,
  SECURITY_IMPLEMENTATION_GUIDE, CONTROLLER_INDEX) — each repointed to
  the surviving equivalent (development.md, techstack.md, api.md,
  security.md, TROUBLESHOOTING.md) or dropped where none exists.
- techstack.md: rewritten — it described PHP 8.4/Symfony 7.3, the
  Encore/Stimulus/Sass frontend and Psalm/PHP_CodeSniffer tooling; now
  documents the actual stack (PHP 8.5, Symfony 8.1, SolidJS/Vite/bun,
  PHPUnit 13, PHPStan/PHPat, Playwright/Vitest).
- api.md: /scripts/timeSummaryForJira documented a {script, version}
  object; the endpoint returns the instance's summary base URL as a
  JSON string — document reality. README.rst now points at the actual
  userscript file (public/scripts/timeSummaryForJira.js).
- testing.md / CODE_EXAMPLES.md: examples referenced
  App\Service\Integration\JiraWorklogService and Jira\JiraOAuthApi —
  the real classes are Jira\JiraWorkLogService and
  Jira\JiraOAuthApiService. PHPUnit 12 → 13.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@CybotTM CybotTM force-pushed the chore/post-extjs-consistency branch from ee52d56 to 029f881 Compare July 2, 2026 01:16
CybotTM added 3 commits July 2, 2026 03:34
Drops the config shapes of the removed mailer/form/messenger surfaces.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Review feedback on #490:

- export.csv.twig: the per-row externalLabels loop emitted a variable
  number of columns, misaligning rows against the single 'Andere
  Labels' header column — join the labels into that one column
  (gemini-code-assist).
- error403.html.twig: add plain-color fallbacks before every
  light-dark() declaration so browsers on the documented FAQ baseline
  (Chrome 90 / Firefox 88 / Safari 14) keep a readable page
  (copilot).
- theme-init.html.twig: the docblock now names the error pages among
  its consumers and states the button wiring no-ops without the header
  (copilot).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
The gate was vacuous since its wiring switched to
'bin/phpstan analyze config/quality/phpat.php': that invocation analyzed
the rule FILE as code under the main config — PHPat discovers rules
exclusively through DI services tagged phpat.test (TestExtractor), and
nothing ever registered the test class, so zero rules ran.

- Move the rules to tests/Architecture/ArchitectureTest.php (PSR-4
  autoloadable; PHPStan's DI must instantiate the service before
  bootstrap files load).
- Add config/quality/phpat.neon: a standalone level-0 pass over src/
  with the phpat.test service registration; built-in PHP classes are
  ignored (they are not architecture).
- Calibrate the allow-lists to the codebase's actual architecture
  (verified empirically: the previous aspirational lists produced 1174
  violations, including controllers depending on their own
  BaseController). The contradicted 'controllers must not access
  repositories' rule is dropped — repository injection into invokable
  actions is the codebase's established pattern (33 sites).
- Rewire composer analyze:arch, the captainhook action and the CI step
  to '-c config/quality/phpat.neon'.

Verified live by experiment: a probe controller depending on PHPUnit is
reported and the pass returns to [OK] once removed. src/ is green.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
CybotTM added 2 commits July 2, 2026 03:47
Derived with a placeholder-aware literal search across every tracked
file (dynamic trans($e->getMessage()) feeders included): none of these
17 strings is built anywhere anymore. 'Every user must belong to at
least one team.' survives in its corrected form in validators.de.yml
(the validator translates via the validators domain and the constraint
message carries no trailing period).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
SonarCloud php:S1192 on the new ArchitectureTest (App\Dto, App\Exception
and App\Model each repeated across allow-lists) — extend the file's
existing constants idiom.

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@sonarqubecloud

sonarqubecloud Bot commented Jul 2, 2026

Copy link
Copy Markdown

@CybotTM CybotTM merged commit 671421f into main Jul 2, 2026
26 checks passed
@CybotTM CybotTM deleted the chore/post-extjs-consistency branch July 2, 2026 01:58
CybotTM added a commit that referenced this pull request Jul 2, 2026
## Description

Enable CSRF protection on logout. The old `enable_csrf: false` carried
the comment "because ExtJS frontend uses simple link for logout" — the
ExtJS shell is gone, and with the stateless CSRF setup already used by
the login form, protecting the plain logout links costs nothing at the
UX level while blocking cross-site forced logout.

Mechanics (verified against the installed Symfony 8.1 vendor code): with
`stateless_token_ids: [authenticate, logout]`, `csrf_token('logout')`
renders a constant marker and validation is purely origin-based —
`Sec-Fetch-Site: same-origin` (primary), `Origin`/`Referer` fallback. A
real user clicking a logout link passes; a cross-site `<img>`/link/form
hard-fails.

## Type of Change

- [x] New feature (non-breaking change that adds functionality)

## Related Issue

Follow-up to #490 (which removed every other ExtJS reference; this PR
retires the last one).

## Changes Made

- `security.yaml`: `enable_csrf: true` on the logout listener; comment
states the actual mechanism instead of blaming the removed ExtJS
frontend.
- All logout triggers use Symfony's `logout_path()`/`logout_url()` — the
`LogoutUrlGenerator` appends `_csrf_token` automatically and stays
correct if the flag is ever toggled: header badge + mobile drawer, 403
page, and the SPA's `logoutUrl` (command palette).
- `AccessDeniedSubscriber`: the remember-me/not-fully-authenticated case
now logs out programmatically (`Security::logout(false)`) instead of
redirecting through `/logout` — a browser following that redirect from
an address-bar navigation carries no same-origin fetch metadata and
would hit the CSRF wall (the empirical weak spot identified during
design).
- e2e logout flows click the link instead of `page.goto()` (CDP
navigations send `Sec-Fetch-Site: none` and would fail even though real
users pass); the tests also assert the link carries the token.
- Functional tests send `HTTP_SEC_FETCH_SITE: same-origin` (BrowserKit
emits no fetch metadata); a new test pins the 403 for tokenless logout
requests; the remember-me tests assert the new direct-to-login flow.

## Testing

- [x] Unit tests green locally (1557, incl. two new subscriber tests:
logout response used as-is + null fallback)
- [x] phpstan level 10, cs-fixer, phpat, twig lint, `lint:container`
(dev/prod/test) — all green locally
- [ ] Functional + e2e — CI (the test env inherits `enable_csrf: true`
via Symfony's config merge; the click-based e2e logout genuinely
exercises CSRF-on logout)

## Code Quality

- [x] Code follows project coding standards
- [x] Self-review completed
- [x] Documentation updated (config comment)
- [x] No breaking changes for real users (bookmarked bare `/logout` URLs
now 403 by design — standard Symfony logout-CSRF behavior)

## Migration Notes

None for deployments. Users who bookmarked the bare `/logout` URL get a
403; logging out via the UI works unchanged.

## Checklist

- [x] I have read the
[CONTRIBUTING](https://github.com/netresearch/timetracker/blob/main/CONTRIBUTING.md)
guidelines
- [x] I agree to follow the [Code of
Conduct](https://github.com/netresearch/timetracker/blob/main/CODE_OF_CONDUCT.md)
CybotTM added a commit that referenced this pull request Jul 2, 2026
…ide with screenshots, ADR record, agent rules (#494)

## Summary

Full documentation overhaul: a verified-facts rewrite of the guides, a
new illustrated user guide, a corrected ADR record, refreshed agent
rules, an extended in-app Help page, and a full-structure README.

A 5-domain audit against the actual code found that, beyond routine
drift, several large docs (DEPLOYMENT_GUIDE, CODE_EXAMPLES,
TROUBLESHOOTING, configuration, development, testing, and ADRs 003–013)
described **infrastructure this application never had** — Redis layers,
JWT auth, `/api/v1`, ParaTest/Panther suites, ~50 invented env vars and
`app:*` console commands. Everything replaced here was re-derived from
`Makefile`, `composer.json`, `compose.yml`, `.env*`, `config/`, `src/`
and the real CI workflows; every command, route, env var and version in
the new docs was verified to exist.

## What's in here

- **New [user guide](docs/user-guide.md)** covering every feature, with
20 English 1440px screenshots — including the QoL features (dark mode,
compact density, left/right sidebar navigation, Alt shortcut badges);
[features.md](docs/features.md) and [FAQ.md](docs/FAQ.md) rewritten for
the current SolidJS UI
- **README expanded to a full project page**: TOC, About, grouped
features, screenshot gallery, first-login credentials for the dev stack,
key-config table, mermaid architecture diagram,
Usage/Testing/Deployment/Contributing/Security sections
- **Developer guides rewritten** ([development](docs/development.md),
[configuration](docs/configuration.md), [testing](docs/testing.md)) —
~5,300 lines of fiction replaced by ~1,300 verified lines; configuration
now documents the real ticket-system admin fields (SERVER OAuth 1.0a vs
CLOUD OAuth 2.0) and internal-Jira ticket mirroring
- **Ops docs rewritten** ([deployment](docs/DEPLOYMENT_GUIDE.md),
[troubleshooting](docs/TROUBLESHOOTING.md)) from `compose.yml` /
`docker-bake.hcl` / the publish workflow
- **ADR record corrected**: index now lists all 17 ADRs; dated *reality
notes* on ADRs whose bodies describe never-built infrastructure (bodies
kept as history); new
[ADR-015](docs/adr/ADR-015-php-8-5-symfony-8-upgrade.md) (PHP
8.5/Symfony 8), [ADR-016](docs/adr/ADR-016-solidjs-frontend-rewrite.md)
(ExtJS→SolidJS, #470/#490),
[ADR-017](docs/adr/ADR-017-jira-cloud-oauth2.md) (dual-mode Jira auth,
#416)
- **API/internals references** (api.md, DTO/EVENT/REPOSITORY docs)
aligned with the code — all 65 endpoint headings now map to real routes
- **AGENTS.md**: drift fixed (PHPStan 10, PER-CS), new scoped files for
`frontend/` and `e2e/`, `CLAUDE.md` symlinks restored at all five levels
- **In-app Help** (`/ui/help`): new "The pages" section (EN+DE) and a
link to the user guide — lint/typecheck/346 Vitest tests green
- **CONTRIBUTING**: the enforced-but-undocumented DCO sign-off
requirement is now documented
- **Removed**: `README.rst` (stale duplicate), `TASKS.md` (abandoned
2025 notes), `docs/CODE_EXAMPLES.md` (68 KB of examples for services
that never existed); all references updated

## Review

Copilot raised 2 inline comments (PER-CS leftover in src/AGENTS.md,
404ing v4_EOL release link) — both fixed in 41fee70, threads replied to
and resolved. Gemini reviewed with no findings.

## Verification

- Every documented `make`/`composer`/`bin/console` command checked
against `Makefile`, `composer.json` scripts and `src/Command/`
- All relative links and image references across 58 markdown files
resolve
- ADR PR/commit references verified via GitHub (#416, #470, #490)
- Frontend change: `bun run lint`, `bun run typecheck`, `bun run test`
(346 passing), page visually verified in the running e2e stack
- Host unit suite green (1557 tests) via the CaptainHook pre-commit gate
on every commit

## Follow-ups (out of scope, found during verification)

- `docker/nginx/default.conf` forwards to `phpfpm:9000` but the prod
compose service is named `app` — the deployment guide documents the
alias workaround; compose.yml should probably gain a `phpfpm` network
alias
- The admin Users form still offers a `CTL` user type
(`frontend/src/admin/entities.ts`) that `UserType::from()` rejects
- Jira Cloud OAuth 2.0: data model + admin form exist (#416), but the
runtime 3LO flow is not implemented yet (`JiraAuthenticationService` is
OAuth1-only) — ADR-017 records this honestly
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants