Files
amare/CLAUDE.md
manoel freitas 1e215ac3d2
Some checks failed
CI / unit (push) Has been cancelled
CI / feature (push) Has been cancelled
CI / browser (push) Has been cancelled
CI / container (push) Has been cancelled
CI / static (push) Has been cancelled
docs: alinhar remotes e registry para Gitea
Origin e deploy passam a documentar git.hellomanoel.com;
GitHub/GHCR ficam como legado/backup.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 14:35:20 -03:00

6.6 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).

Design principles: DRY & YAGNI (not TDD)

Write for the problem at hand, not an imagined future. DRY: extract and reuse only once logic is genuinely duplicated in more than one place. YAGNI: no speculative abstraction, configurability, or layers — add them only when a concrete requirement demands it. Prefer the simplest thing that solves the current requirement; avoid over-engineering and premature extraction. Tests are verification, not a design driver: write them to cover behavior already implemented, not as a front-loaded TDD ceremony.

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 remotes — Gitea origin, GitHub legacy

origin is git@git.hellomanoel.com:manoel-freitas/amare.git (self-hosted Gitea). CI/CD and the container registry live there (git.hellomanoel.com). Verify SSH with ssh -T git@git.hellomanoel.com → should greet Hi there, manoel-freitas!.

The remote named github is the legacy mirror git@github.com:manoel-freitas/amore-site.git. Push there only when intentionally syncing the backup. That GitHub account still needs ~/.ssh/id_github_pessoal (or an equivalent key) when the machine's default identity is a different GitHub user (manoel-freitas-neto) that cannot see the repo.

  • Prefer plain git push / git push origin <branch> against Gitea.
  • gh talks to GitHub only. Use the Gitea web UI or API for PRs on amare. If you still need gh against the legacy remote, confirm gh auth status shows manoel-freitas.

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.