Files
amare/CLAUDE.md

6.3 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.

Deterministic test support

APP_FROZEN_NOW configures CarbonImmutable::setTestNow() through AppServiceProvider::freezeClockWhenConfigured() outside production. VisualContentSeeder provides deterministic image/content fixtures for tests that explicitly need them. Neither setting is a browser-CI global default.

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.