Attainly/ai/shared.md

239 lines
9.9 KiB
Markdown

# Shared rules
Rules that apply to both backend and frontend work in this repository.
Stack-specific guides (`backend-context.md`, `frontend-context.md`) extend
these rules.
## Project state
- Attainly is in early development.
- Attainly helps users break hierarchical goals into scheduled assignments,
complete daily work, and track progress toward a target date.
- Schedules target whole sets. Users choose an element kind as the assignment
granularity. To schedule a subset, create a separate named set containing
that subset.
- Schedules are immutable historical snapshots. Copy the set name and each
assignment's name, kind, and full path when creating a schedule.
- Persisted schedules must not reference their source set, and persisted
assignments must not reference their source elements. Later source edits or
deletions must not change an existing schedule.
- Schedule recalculation must preserve completed work while redistributing
unfinished assignments across the remaining dates.
- Planned features in `README.md` are future ideas, not authorized scope.
- The Laravel backend exists under `backend/`.
- The standalone Vue frontend exists under `frontend/website/`.
- Keep the frontend independent from the backend. Do not add backend-driven
rendering, asset delivery, or build integration unless the user explicitly
asks for it.
- PostgreSQL is the development and runtime database.
- PHPUnit uses in-memory SQLite for isolated tests.
## Process (TDD)
0. Before editing any file, ensure you are working in a dedicated git
worktree, not the main checkout (`git status` and `git worktree list`).
If on `master`/`main` or in the main working directory, create a worktree
first (see Branching).
1. Write the test first.
2. Run the test to confirm it fails for the expected reason.
3. Commit the failing test. The test commit must precede the implementation
commit, not merely appear earlier in the implementation diff.
4. Implement the smallest change that makes the test pass.
5. Run the test to confirm it passes.
6. Commit the implementation.
7. Repeat for each new behavior.
Use judgment for changes that cannot meaningfully be test-driven, such as
documentation-only edits or declarative environment configuration. Validate
those changes with the most relevant parser, formatter, dry run, or check.
## Maintaining these instructions
- Treat user steering as durable when it corrects or establishes a reusable
project rule for workflow, architecture, conventions, validation, safety,
or scope.
- When durable steering is received during work, update the appropriate
`ai/*.md` file in the active worktree before completing the task. Do not
wait for a separate request to maintain the instructions.
- Put repository-wide rules in `shared.md` and stack-specific rules in the
matching backend or frontend guide.
- Merge new guidance into the existing rule set. Keep it concise, remove
duplication, and resolve conflicts between shared and stack-specific text.
- Do not persist task-specific scope, temporary directions, secrets,
environment incidents, or instructions that conflict with higher-priority
guidance.
- Commit a durable instruction update separately from implementation unless
the task itself is solely an instruction change.
## Approval discipline
- Treat dependency installation, tests, static analysis, formatting, linting,
type checking, and builds as routine project operations. Run them without
asking for advance approval when they stay within the requested scope and
sandbox boundaries.
- If an essential operation is blocked by sandbox or network policy, batch the
necessary provisioning or validation into one narrowly scoped approval
request. Prefer a reusable command prefix when it can be safely limited to
the required tool and operation.
- Do not request repeated approvals for commands that fit a previously
approved scope.
- Do not prefix routine commands with per-command cache environment overrides.
Configure cache locations once in Codex, direnv, or the project environment.
Use temporary overrides only to diagnose a cache-specific problem.
- Truly destructive actions, external writes, main-stack control, and
operations outside the workspace still require explicit approval.
## Running processes
- The main checkout owns the canonical stack on the default ports. Assume it
is the user's stack. Do not start, restart, or stop it unless the user asks.
- A worktree owns its own isolated stack. The flake shell hook assigns a
deterministic port offset and creates worktree-local PostgreSQL state.
- Start a worktree stack only when runtime or integration validation requires
it. Start it once, reuse it throughout validation, and stop it once when
finished.
- PHPUnit, frontend formatting, linting, type checking, and production builds
do not require services. The Cypress completion suite does require the
worktree stack.
- When worktree stack control is necessary, operate it directly. Do not ask
the user to start or stop worktree services.
- Never use `process-compose -t=false` for a detached stack. It can leave an
orphaned PostgreSQL process holding the data directory.
- Non-interactive agent shells do not automatically load direnv. Bare project
commands can use missing tools, default ports, or paths from the main
checkout.
- Run PHP, Composer, Artisan, npm, tests, builds, database clients, and
services through the development environment.
- Resolve the direnv target from the worktree containing the current working
directory. Never target the main checkout or a different worktree:
```sh
direnv exec "$(git rev-parse --show-toplevel)" <command>
```
- Git and environment-neutral read-only inspection do not need direnv.
- Start and stop a worktree stack from its root:
```sh
direnv exec "$(git rev-parse --show-toplevel)" process-compose up -D
direnv exec "$(git rev-parse --show-toplevel)" process-compose down
```
- Run backend commands from `backend/` and frontend commands from
`frontend/website/`. The direnv target remains the worktree root.
- When a normally valid check fails because a required service is down,
surface the environmental failure. Do not skip the check, change databases,
or claim the work is complete.
- PHPUnit is self-contained and uses in-memory SQLite. It does not require the
PostgreSQL stack unless a future test explicitly targets real PostgreSQL.
## Code style
- Keep lines at or below 80 columns where practical. Do not split short,
readable lines unnecessarily.
- Use explicit, descriptive variable names. Do not use single-letter or
unexplained abbreviated names.
- Explore the codebase and inspect similar files before implementing a new
pattern.
- Never use em dashes in code, comments, or docblocks. Use hyphens.
- Always use braced control-statement bodies, including early returns:
```ts
if (identifier === null) {
return null
}
```
## Git commit style
- Use present-tense, imperative subjects: `add`, `create`, `wire`, `fix`,
`test`.
- Keep subjects lowercase and short, normally three to six words.
- Match patterns in the existing git history.
- Do not add AI or tool coauthor trailers.
- Add a body when the subject cannot explain non-obvious motivation or
multi-file coordination.
- Wrap bodies at approximately 72 columns and separate them from the subject
with a blank line.
## Git commits
- Commit tests before implementation.
- Keep one logical change per commit. A logical change may span multiple
files.
- Commit each meaningful step rather than batching unrelated work.
- Do not include drive-by formatter or linter changes. Restore them or land
them as a separate formatting commit.
- If a check fails on untouched code, do not bundle an unrelated fix. Report
the baseline failure or handle it as its own explicitly scoped change.
## Branching
- Never implement features directly in the main checkout or on
`master`/`main`.
- Use a dedicated worktree under `<repo-root>/.worktrees/<branch>`.
- Create it with:
```sh
git worktree add \
"$(git rev-parse --show-toplevel)/.worktrees/<branch>" \
-b <branch>
```
- Use descriptive kebab-case branch names, optionally prefixed with a type
such as `feature/` or `fix/`.
- Keep worktree names short, preferably no more than about 20 characters.
PostgreSQL Unix socket paths are limited in length.
- Provision a fresh worktree through direnv:
```sh
direnv allow <worktree>
direnv exec <worktree> true
```
- Never symlink `backend/vendor` or `frontend/website/node_modules` from
another checkout. Dependencies and generated files must remain
worktree-local.
- The shell hook installs backend dependencies but not frontend dependencies.
Install frontend dependencies from `frontend/website/`:
```sh
direnv exec "$(git rev-parse --show-toplevel)" npm install
```
Do not push anything. Make commits as the TDD workflow requires.
## Before completing a change
A change is not complete until the worktree stack is ready and the unified
gate passes against that worktree:
1. Start the stack detached from the worktree root:
```sh
direnv exec "$(git rev-parse --show-toplevel)" \
process-compose up -D
```
2. Poll `process-compose process list` until every process is running and
ready.
3. Run the complete gate from the worktree root:
```sh
direnv exec "$(git rev-parse --show-toplevel)" just test-all
```
4. Do not hand-assemble a substitute from focused commands. `test-all` runs
frontend format and lint checks, frontend type checking, Larastan, the
production build, PHPUnit, and Cypress in fail-fast order.
5. Everything must pass before completion. Report exact baseline or
environmental failures rather than hiding them.
6. If the stack was started only for validation, stop it when finished:
```sh
direnv exec "$(git rev-parse --show-toplevel)" process-compose down
```
Focused `just` recipes are for iteration only. For Nix or shell-hook changes,
also run `nix fmt` and `nix flake check`. For service configuration changes,
also run `process-compose --dry-run`.