Files
amare/CLAUDE.md
Manoel Freitas 2e43fdeb04 chore: usar America/Sao_Paulo como timezone da aplicação (#42)
A cidade de atuação é São Paulo, garantida por teste em quatro lugares e
exigida por openspec/specs/site-settings/spec.md. O identificador de
timezone, porém, era America/Fortaleza. O identificador passa a
acompanhar o negócio.

A mudança não altera comportamento. America/Sao_Paulo e
America/Fortaleza são UTC-3 o ano inteiro desde que o horário de verão
brasileiro foi extinto — verificado para janeiro, março e dezembro de
2026, idênticos ao segundo. Nada renderizado muda, o relógio congelado
dos testes visuais usa offset absoluto (-03:00) e os baselines seguem
válidos.

O motivo de mexer é outro: a divergência entre o timezone e a cidade
custou tempo real. Uma sessão anterior a interpretou como drift e
"corrigiu" a SPEC no sentido errado, mudando o documento normativo para
Fortaleza em vez de olhar o que o negócio é. Com os dois valores dizendo
São Paulo, não há mais o que interpretar.

Escopo: config/app.php, .env.example, os dois pontos do ci.yml,
SPEC.md (§0, §13.5, §15.4), README.md, docs/deployment/dokploy.md,
CLAUDE.md, openspec/config.yaml, openspec/specs/visual-regression/spec.md
e o withTimezone do VisualRegressionTest.

Intocados de propósito: as asserções que garantem que Fortaleza não
aparece como cidade de operação, em PublicPagesTest, SiteSettingsTest,
ContentSeederProductionGatingTest e openspec/specs/site-settings. Essas
tratam de cidade, não de fuso, e continuam corretas.

Co-Authored-By: Claude noreply@anthropic.com
AI-Assisted: yes
AI-Tool: claude-code

Co-authored-by: manoel.neto <manoel.neto@creditas.com>
2026-08-10 11:37:03 -03:00

7.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

@AGENTS.md

If the import above did not load, read AGENTS.md at the repo root now — it is the primary contract.

AGENTS.md (imported above) is the primary contract: structure, commands, style, testing, husky hooks, commit/PR rules. This file records the cross-file architecture and environment facts that are not obvious from any single file.

Non-negotiables (repeated from AGENTS.md because breaking them is expensive)

  • Never modify or commit from the primary working tree on main. Create a worktree per branch: git worktree add -b <branch> <path> main.
  • Every project-owned PHP file starts with declare(strict_types=1); immediately after <?php.
  • Browser tests are CI-only (they run against a FrankenPHP container built by docker build, not artisan serve).

Environment note

PHP and Composer are not on PATH in this environment, and vendor/ and node_modules/ are absent. Every composer … / php artisan … command in AGENTS.md and README.md assumes a PHP 8.4+ runtime with Composer 2 installed. Verify the toolchain before promising a command ran.

Git remote auth — two GitHub accounts

origin is git@github.com:manoel-freitas/amore-site.git, owned by the manoel-freitas account. The machine's default SSH identity is a different account (manoel-freitas-neto) that cannot see this repo, so pushes fail with ERROR: Repository not found. — an access error that reads like a missing repo.

  • Correct key: ~/.ssh/id_github_pessoal. Verify with ssh -i ~/.ssh/id_github_pessoal -o IdentitiesOnly=yes -T git@github.com → should greet Hi manoel-freitas!.
  • The repo has core.sshCommand = ssh -i ~/.ssh/id_github_pessoal -o IdentitiesOnly=yes set locally, so plain git push works. If that config is lost, restore it instead of editing the remote URL.
  • gh authenticates separately, by token rather than SSH key. As of 2026-08-10 it is logged in as manoel-freitas, so gh pr create / gh repo view work. Confirm with gh auth status before assuming: if it reports manoel-freitas-neto, that account cannot see this repo and every gh call fails on it. Recovering needs an interactive gh auth login (or gh auth switch with both accounts added), so ask the user to run it.

Request spine for the public site

Adding or changing a public page follows one path — controllers never query models directly:

routes/web.php
  → App\Http\Controllers\PublicSite\*Controller   (thin; injects a query object)
    → App\Application\Queries\Marketing\*         (invokable, final; owns all Eloquent access)
      → App\Application\Data\*                    (DTO: HomeContent, PageMeta)
        → resources/views/pages/*.blade.php

HomeController + GetHomeContent together show the shape. Page-level SEO is built with PageMeta::forPage(canonical:, settings:, jsonLd:).

AppServiceProvider::boot() registers a View composer on layouts.public that auto-injects siteSettings and pageMeta when the view didn't supply them — new pages do not have to pass them manually.

Site-wide content is a singleton row reached via SiteSetting::instance(). Publication state comes from the HasPublication concern (->published() scope).

Architecture boundary — what is actually enforced

tests/Architecture/DomainBoundariesTest.php enforces only:

  • App\Domain uses strict types and never dd/dump/die.
  • App\Domain never depends on App\Filament or App\Livewire.

App\Application is not covered by that rule. app/Domain/ currently holds a single placeholder (DomainModule.php); business reads live in app/Application/Queries. Extend the arch test when you add a boundary.

Visual regression — read before touching baselines

  • Baselines are committed .snap files under tests/.pest/snapshots/Browser/VisualRegressionTest/.
  • tests/Browser/Screenshots/ is gitignored — it only holds diff output.
  • Regenerate with composer visual:update.
  • Baselines are CI-parity artifacts. CI runs the browser suite against a docker build-produced FrankenPHP container (see the browser job in .github/workflows/ci.yml), so baselines regenerated on macOS against a local server will be rejected by CI. Commit 4578457 exists because of this.

Determinism relies on three cooperating pieces:

  • APP_FROZEN_NOWCarbonImmutable::setTestNow() in AppServiceProvider::freezeClockWhenConfigured() (no-op in production).
  • Database\Seeders\VisualContentSeeder::FROZEN_NOW — the value the browser tests and the CI job both pin to.
  • Tests\Support\StableScreenshot — forces Arial, disables transitions/animations, scrolls the page to settle lazy images, and avoids the flaky networkidle wait.

Other things that bite

  • Livewire/Filament temp uploads are pinned to the local disk when FILESYSTEM_DISK=r2, because the S3 driver would make the browser PUT straight to R2 and hit CORS. Final media still lands on r2 via App\Support\PublicImageUploadRules. Set LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK explicitly to override.
  • Contact form is rate limited: named limiter contact-briefing, 5/min per IP, registered in AppServiceProvider and applied in routes/web.php.
  • Filament 5 nested resource layout: resources are split into app/Filament/Resources/<Resource>/{Pages,Schemas,Tables,RelationManagers} rather than a flat resource class. Follow the existing shape in Resources/PortfolioCases/.
  • Everything user-facing is pt-BR: routes are /servicos, /portfolio, /portfolio/{slug}, /sobre, /privacidade, /contato. APP_LOCALE=pt_BR, APP_TIMEZONE=America/Sao_Paulo (config/app.php:68).
  • Design tokens live in resources/css/tokens.css (Heritage Editorial; see DESIGN.md). tests/Feature/PublicSite/HeritageEditorialTokensTest.php reads that file and asserts the exact hex values, EB Garamond, zero border radii, --amare-container-max: 1120px, and the absence of shadow tokens — so any token edit is a deliberate test change too. Motion lives in resources/js/motion.js and is asserted by tests/Feature/PublicSite/MotionMarkupTest.php + tests/Browser/MotionTest.php.

Navigating the normative docs

  • SPEC.md is the product source of truth and is ~2600 lines. Never read it wholegrep -n '^## ' SPEC.md and read the numbered section you need (e.g. 7 functional requirements, 8 domain model/DB, 9 technical architecture, 13 test strategy, 15 FrankenPHP deploy).
  • openspec/ is the channel for planned change: openspec/specs/<capability>/spec.md for current capabilities, openspec/changes/<change>/{proposal,design,tasks}.md for in-flight work. openspec/config.yaml holds the precedence rule (product owner > SPEC.md > ADRs > tests > conventions) and repo-wide constraints (YAGNI, money as BIGINT centavos, no generic repositories/BaseService).
  • PRODUCT.md for positioning, DESIGN.md for the design system, docs/conventions/php-strict-types.md, docs/deployment/dokploy.md for the deploy runbook.