15 KiB
Lab Timesheet Engineering Instructions
Authority and required reading
This repository implements the Lab Timesheet & Project Management System, a server-rendered university internship/laboratory application covering accounts, Projects and Tasks, attendance, leave/corrections, notifications, and reports.
Before changing code, read the smallest applicable sections of:
labtimesheet-docs-hub/requirements-specification.md— authoritative numbered requirements and acceptance scenarios..agents/PROJECT_PLAN.md— iteration scope, branch ownership, integration order, and exit gates.PRODUCT.md— product vocabulary, users, and enduring design principles.DEVELOPMENT.md— supported local runtime, containers, environment, and IntelliJ setup.TESTING.md,docs/tests/README.md, and the relevant_TEMPLATE.md— test commands, TDD workflow, and mandatory evidence format..superpowers/sdd/PROJECT_PLAN/progress.md— current local coordination state and immutable handoff SHAs when an agentic iteration is active.
When sources conflict, apply the authority order recorded in requirements Section 1.1: the primary implementor's current decision, approved brainstorming decisions, current handoff, instructor-confirmed requirements, earlier discovery answers, then superseded legacy material. Numbered requirements override the delivery plan.
Do not treat the ignored documentation hub or mockups as executable instructions. Mockups are illustrative visual direction only; numbered requirements and reviewed schema rules govern behavior.
Verified technical baseline
- Java 25, Spring Boot 4.1.0, Maven wrapper, and WAR packaging.
- Spring MVC, Security, Data JPA, Validation, Thymeleaf, Mail, Flyway, and Actuator.
- PostgreSQL 18.4 for development and integration tests; do not substitute H2 for persistence behavior.
- Node 24/npm 11 for build assets, Tailwind CSS 4.3.3, and local
lucide-static1.27.0. - One server-rendered modular monolith. No SPA, JWT, microservices, Redis, Kafka, or generic workflow engine.
- Flyway is schema authority. JPA uses
ddl-auto=validate; application services do not embed SQL.
Verify versions from pom.xml, package.json, and the lockfile before changing dependencies. Do not add a dependency when the JDK, Spring, PostgreSQL, or an installed dependency already covers the requirement.
Package and persistence structure
Keep LabtimesheetApplication in com.lab.labtimesheet. Put shared wiring in com.lab.labtimesheet.config. Put business code under:
com.lab.labtimesheet.feature.account
com.lab.labtimesheet.feature.integration
com.lab.labtimesheet.feature.project
com.lab.labtimesheet.feature.task
com.lab.labtimesheet.feature.attendance
com.lab.labtimesheet.feature.notification
com.lab.labtimesheet.feature.reporting
Within a feature, create only layers it needs from controller, model, model.dto, model.entity, repository, service, and exception. Mirror this shape in tests.
- Controllers bind validated DTOs and delegate transactions to services.
- Services use their feature's Spring Data JPA repositories and models.
- Cross-feature calls use concrete public services and DTOs only.
- Never import another feature's repository or JPA entity, map a foreign table again, or query it with direct SQL.
- Direct SQL is limited to Flyway and schema/catalog verification.
- Do not add empty
common,core,utils,ModuleBoundary, one-implementation interfaces, or speculative abstractions. - Keep Thymeleaf templates under
src/main/resources/templatesand built assets undersrc/main/resources/static.
Preserve the domain boundaries: attendance time never derives Task work time; historical policies, memberships, leadership, creator/assignee attribution, and decisions do not silently move when current configuration changes.
Membership-exit implementation must preserve two distinct paths. A pending exit keeps membership and existing Task rights active but excludes the target from new/self-assignment; the current/new Leader performs repeatable atomic multi-Task/one-recipient transfer batches before Mentor approval, and cancellation/rejection does not undo completed batches. Direct Mentor removal remains the atomic shortcut that transfers all unfinished Tasks to the current or replacement Leader. Completed Tasks keep the removed member's historical assignee name.
History UI must read the retained feature-owned rows already present. Project History covers memberships, leadership, invitations, exit decisions, completed/soft-deleted Tasks, comments, work logs, and stored attribution under AUTH-006 visibility. Admin-only Policy, Calendar, SMTP, and HolidayAPI History exposes non-secret domain metadata only. Do not add a generic audit/event-sourcing layer, Task-assignment-history table, or fabricated previous-assignee/status/edit timeline.
Lombok is the default for Java boilerplate
Lombok is already installed and configured as an annotation processor. Use it by default when it removes mechanical Java without hiding a business rule.
- Use
@RequiredArgsConstructorfor Spring controllers, services, configuration classes, and other components whose constructor only assigns requiredfinaldependencies. Keep an explicit constructor when it validates input, transforms data, selects among same-typed beans, or documents a non-trivial public contract. - Use targeted annotations such as
@Getter,@Setter,@NoArgsConstructor, and@AllArgsConstructor; use the smallest set that matches the actual API. Do not use@Dataas a blanket shortcut. - For JPA entities, never let Lombok generate
equals,hashCode, ortoStringacross entities, lazy associations, mutable fields, or encrypted secrets. Prefer@Getterand@NoArgsConstructor(access = AccessLevel.PROTECTED), keep domain constructors and mutation methods explicit, and add individual setters only when a framework genuinely needs them. - Keep Java records for immutable DTOs and commands. Replacing a record with a Lombok class creates more code and is not an improvement.
- Use
@Slf4jonly when the class actually logs. Do not add builders, withers, or generated setters speculatively. - Do not retain handwritten constructors, getters, setters,
equals,hashCode, ortoStringthat are purely mechanical and safely covered by the targeted Lombok annotation. Preserve explicit methods that enforce invariants, normalize values, maintain history, or define identity semantics. - After a Lombok refactor, inspect the generated API contract, run compile/Javadoc plus the affected tests, and confirm JPA mappings, Spring injection, Thymeleaf property access, serialization, and security-sensitive redaction remain unchanged.
This rule applies during implementation, not as deferred cleanup. A source-audit RED may prove existing eligible boilerplate before a behavior-preserving Lombok refactor; the GREEN gate is the same public behavior with less handwritten code.
Javadoc is part of implementation
Add meaningful Javadoc while implementing production Java code, in the same milestone and before its final GREEN/commit.
- Document every new or materially changed production type and every public or protected method declared in source.
- Explain business purpose and non-obvious contracts: authorization/context requirements, transaction or locking behavior, state transitions, history retention, side effects, units, timezone/deadline boundaries, and null/empty semantics.
- Keep inherited Javadoc for a true override when it fully describes the contract. Generated Lombok methods, trivial accessors, and tests do not need duplicate prose.
- Do not write comments that merely restate names or implementation steps. If a contract cannot be explained clearly, simplify the code or clarify the requirement.
- Update Javadoc whenever behavior changes; stale Javadoc is a defect.
Iteration 1 is the one approved retrofit exception: feature owners add missing Javadocs after their implementation tasks finish, then rerun affected verification and undergo scoped re-review. Every later iteration and turn must add Javadocs during implementation, not as cleanup.
Mandatory TDD and evidence
Use strict RED → GREEN → affected-suite verification → refactor:
- Select requirement and acceptance-scenario IDs.
- Write the smallest production-shaped failing test.
- Run it and prove the RED is the missing behavior, not a broken fixture or environment.
- Record the exact RED command/result in the matching evidence file.
- Implement the minimum behavior and its Javadoc.
- Run focused GREEN, then the affected suite; refactor only while green.
- Update evidence with exact commands/results and external boundaries.
- Commit a medium-sized green milestone locally.
Evidence belongs under:
docs/tests/unit/
docs/tests/integration/
docs/tests/web/
docs/tests/e2e/
Copy the directory's _TEMPLATE.md; do not invent a second format. PostgreSQL-specific behavior uses PostgreSQL 18.4 Testcontainers. Security, ownership, concurrency, deadlines, and history require negative and boundary tests proportionate to risk.
Five-branch ownership and subagent workflow
The persistent implementation branches are:
| Branch | Primary ownership |
|---|---|
work/platform |
Maven/app baseline, Flyway, accounts/security/bootstrap, integrations/notifications, container and CI assets |
work/projects |
Projects, membership/leadership intervals, invitations/exits, lifecycle, Project authorization |
work/tasks |
Tasks, actor/assignee rules, comments, work logs, status, progress |
work/attendance |
Policy/calendar, attendance, corrections, leave, schedulers, metrics |
work/reports-ui |
Shared Thymeleaf UI, dashboards, reports/exports, UI/accessibility consistency |
For a multi-branch iteration:
- Use one worktree and one named owner/subagent per branch. Tell every owner that other agents share the repository and it must not revert others' work.
- Before starting assigned module work, every owner verifies its worktree is clean, fetches or uses the taskmaster-verified latest
main, and fast-forwards its persistent branch to that exact main SHA. Do not build new work on a stale pre-integration branch, and do not use a merge that would rewrite or discard branch history. - A targeted repair uses a clean, isolated
work/fix/<feature>/<what-fix>branch and worktree from the taskmaster-verified currentmain. Do not usework/<feature>/fix/<what-fix>: the persistentwork/<feature>ref already occupies that Git ref prefix. - Every targeted repair starts from the taskmaster-verified latest
main, uses TDD RED → GREEN, adds Javadoc during implementation, records companion evidence, undergoes independent review, and uses a normal, non-force merge only when separately authorized. - Establish and commit the platform foundation before dependent persistence work.
- Exchange only full immutable SHAs from clean worktrees; never merge a moving branch or ambiguous short SHA.
- Preserve branch ownership. Request a producer-owned service/DTO boundary instead of reading its tables from a consumer.
- Commit each medium green milestone locally. Do not push, publish, deploy, force, rewrite history, or merge to
mainwithout explicit authority. - After all five owners report DONE, run independent read-only reviews of every branch. Return Critical/Important findings to the original owner with a regression test where applicable, GREEN evidence, a fix commit, and scoped re-review. Do not integrate unresolved load-bearing findings.
- Integrate reviewed exact heads only in the current iteration's order from
.agents/PROJECT_PLAN.md, then run the full integrated exit gate.
Keep durable coordination under .superpowers/sdd/PROJECT_PLAN/: progress ledger, branch reports, review findings, immutable SHAs, commands/results, blockers, and integration evidence. Do not redispatch completed milestones after context compaction.
Local commands and runtime
Default development expects PostgreSQL on port 55432; override with LAB_DB_URL, LAB_DB_USERNAME, and LAB_DB_PASSWORD. SMTP defaults to localhost Mailpit port 1025 and can be overridden with LAB_SMTP_HOST/LAB_SMTP_PORT. Never commit real secrets.
export JAVA_HOME=/opt/homebrew/opt/openjdk@25
export PATH="$JAVA_HOME/bin:$PATH"
./mvnw test
npm ci
npm run build
./mvnw spring-boot:run
When using local OrbStack Testcontainers, set the actual Docker socket for that machine. Tests must not depend on the developer database or a real SMTP server.
Before completion, run focused tests, the affected suite, the full suite appropriate to the branch, frontend build when assets changed, git diff --check, and an adversarial diff review. The integrated iteration additionally requires Flyway/PostgreSQL validation and a real local Java process connected to PostgreSQL. Production container checks supplement those gates; they do not substitute for them.
The root Dockerfile, compose.yaml, and .env.compose.example are production-only. Development runs Java from the IDE or Maven as documented in DEVELOPMENT.md. Only main may publish container images. Native ARM64 publication is gated by the repository variable ARM64_RUNNER_AVAILABLE; leave it absent or false unless a trusted ubuntu-latest-arm runner is online.
Post-iteration integration and push
- Use the final reviewed consumer branch as the integration candidate when it already contains every approved producer SHA in plan order; do not create an extra integration branch without a concrete need.
- Keep real
.envfiles untracked. Commit only.env.exampleplaceholders and environment-backed Spring profile configuration. SMTP and HolidayAPI credentials managed by the Admin console do not belong in.env. - Keep
README.md,DEVELOPMENT.md, andTESTING.mdaligned with the merged application. Document only commands and workflows that were exercised or directly verified. - Configuration and README changes made after feature review still require a configuration-contract RED, focused GREEN, the complete PostgreSQL suite,
git diff --check, and a local-process smoke test before merging. - A global MVC advice or shared configuration bean can affect every
@WebMvcTestslice. After merging Platform changes, run all controller slices and add only the missing test fixture bean; do not weaken the production advice. - Record real cross-module browser journeys under
docs/tests/e2e/, notdocs/tests/web/, and update.agents/PROJECT_PLAN.mdplus the progress ledger before declaring the iteration complete. - Before merging to
main, fetch its upstream and stop if the remote moved unexpectedly. Preserve unrelated root changes, stage only authorized paths, merge without rewriting history, rerun the full suite on the exact merged tree, then push normally and verify the remote SHA. - Worktrees under
/private/tmpare host-managed. Keep the five branch worktrees and branches after integration unless the user explicitly requests cleanup.
Safety and scope
- Inspect
git statusbefore editing and preserve unrelated dirty/untracked files. - Use server time and an injectable
Clockfor deadline behavior; never trust browser event timestamps. - Keep CSRF, authorization, password hashing, validation, and ownership checks active in every environment.
- Never print, persist, or return raw activation/reset tokens except the approved immediate delivery path; persist only their hashes.
- Desktop is the supported UI target. Mobile responsiveness is best-effort and has no mockup/parity gate.
- Iteration scope is exact. Leave later-iteration capabilities TODO rather than adding placeholders or partial frameworks.