From eebe6094d447311ffb24ad71c73e9478b1a4ca52 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Tue, 24 Mar 2026 20:32:50 -0300 Subject: [PATCH 1/4] docs(architecture): clarify Dependency Injection pattern and repository usage --- docs/ARCHITECTURE_GUIDE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/ARCHITECTURE_GUIDE.md b/docs/ARCHITECTURE_GUIDE.md index f1603d79a..f007d9aab 100644 --- a/docs/ARCHITECTURE_GUIDE.md +++ b/docs/ARCHITECTURE_GUIDE.md @@ -529,8 +529,10 @@ The project adopts an explicit, manual Dependency Injection (DI) pattern for all ### How It Works - **Dependencies are always passed as arguments** to orchestration functions (e.g., logic, use cases), never imported or instantiated directly inside them. -- **Repositories and fetchers** are created at the application layer and injected into logic functions. -- **No direct infrastructure imports** in logic modules: all dependencies must be provided from the outside. +- **Concrete repositories and repository-backed local/localStorage adapters** are created only in the app container or in tests. +- **Application factories accept ready collaborators** such as repositories, CRUD services, or resource fetchers; they do not construct infrastructure defaults internally. +- **UI components consume container-owned modules** through `useContainer()` instead of creating use-cases at module scope. +- **No direct infrastructure imports** in logic modules: all infrastructure-facing dependencies must be provided from the outside. ### Example: Search Module From 316e9d73a2d59ce595aac299f52107909ca8f97b Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Tue, 24 Mar 2026 20:51:04 -0300 Subject: [PATCH 2/4] docs(governance): establish canonical structure, governance, and architectural overview --- .github/COPILOT_SETUP_VALIDATION.md | 16 +- .github/README.md | 24 ++ .../copilot-commit-message-instructions.md | 4 +- .github/copilot-instructions.md | 252 +----------------- .github/prompts/code-review.prompt.md | 4 +- .github/prompts/issues-worktree.prompt.md | 4 +- .github/prompts/pr-reviews.prompt.md | 18 +- .github/prompts/refactor.prompt.md | 6 +- .github/prompts/refine-github-issue.prompt.md | 4 +- AGENTS.md | 69 +++++ CLAUDE.md | 193 +------------- DI-migration-plan.md | 3 + GEMINI.md | 222 +-------------- README.md | 8 + docs/ARCHITECTURE.md | 84 ++++++ docs/ARCHITECTURE_AUDIT.md | 3 + docs/ARCHITECTURE_GUIDE.md | 5 +- docs/BOUNDARIES.md | 94 +++++++ docs/CODESTYLE_GUIDE.md | 5 +- docs/COPILOT_SHORT_GUIDE.md | 9 +- docs/DEPRECATION_PLAN_V0.14.0.md | 3 + docs/DOCS_GOVERNANCE.md | 84 ++++++ docs/README.md | 54 ++++ docs/RECIPE_MIGRATION_AUDIT.md | 3 + ...l-agent-instructions-and-doc-precedence.md | 34 +++ ...urrent-repo-architecture-and-boundaries.md | 35 +++ ...or-handling-and-simplification-defaults.md | 38 +++ docs/adr/README.md | 22 ++ docs/adr/_template.md | 20 ++ docs/{ => archive}/TODO-REPO-DOC.md | 3 + docs/audit_domain.md | 3 + docs/audit_domain_diet.md | 3 + docs/audit_domain_diet_food.md | 3 + docs/audit_domain_diet_recipe.md | 3 + docs/audit_sections.md | 3 + package.json | 3 +- scripts/check-doc-governance.mjs | 180 +++++++++++++ 37 files changed, 848 insertions(+), 673 deletions(-) create mode 100644 .github/README.md create mode 100644 AGENTS.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/BOUNDARIES.md create mode 100644 docs/DOCS_GOVERNANCE.md create mode 100644 docs/README.md create mode 100644 docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md create mode 100644 docs/adr/0002-current-repo-architecture-and-boundaries.md create mode 100644 docs/adr/0003-error-handling-and-simplification-defaults.md create mode 100644 docs/adr/README.md create mode 100644 docs/adr/_template.md rename docs/{ => archive}/TODO-REPO-DOC.md (98%) create mode 100644 scripts/check-doc-governance.mjs diff --git a/.github/COPILOT_SETUP_VALIDATION.md b/.github/COPILOT_SETUP_VALIDATION.md index 2b4237455..ba59b6a34 100644 --- a/.github/COPILOT_SETUP_VALIDATION.md +++ b/.github/COPILOT_SETUP_VALIDATION.md @@ -1,17 +1,17 @@ # GitHub Copilot Setup Validation -This document validates that the macroflows repository follows GitHub Copilot coding agent best practices. +> Doc status: supporting. +> This document records a Copilot-specific setup snapshot. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + +This document validates a Copilot-oriented workspace setup for Macroflows. The current repo-wide source of truth lives in `../AGENTS.md`, while `.github/copilot-instructions.md` now acts as a thin Copilot transport pointer. ## ✅ Completed Setup Checklist ### Core Configuration Files -- [x] **`.github/copilot-instructions.md`** - Main instruction file with frontmatter (`applyTo: "**"`) - - Contains comprehensive coding standards for TypeScript, SolidJS, and Clean Architecture - - Includes project-specific rules (barrel file ban, import conventions) - - Defines error handling patterns for Domain and Application layers - - Specifies testing, validation, and commit message requirements - - References label usage and search feature requirements +- [x] **`.github/copilot-instructions.md`** - Copilot transport pointer with frontmatter (`applyTo: "**"`) + - Points Copilot to the canonical policy in `AGENTS.md` + - Preserves automatic discovery without owning repo policy - [x] **`.github/copilot-commit-message-instructions.md`** - Commit message generation guidelines - Enforces Conventional Commits standard @@ -126,7 +126,7 @@ While the current setup is comprehensive and follows best practices, here are po The macroflows repository successfully implements GitHub Copilot coding agent best practices: -1. ✅ Main instruction file exists and is comprehensive +1. ✅ Copilot transport pointer exists and forwards to canonical docs 2. ✅ Commit message generation is properly configured 3. ✅ Scoped instruction files are available 4. ✅ Extensive prompt library exists (28 prompts) diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..52ce8e8c6 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,24 @@ +# GitHub Workspace Assets + +> Doc status: supporting. +> This document explains the `.github/` workspace assets. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + +## Purpose + +This directory contains GitHub and Copilot-specific workspace assets used by the Macroflows repository. + +## Contents + +- `copilot-instructions.md`: Copilot transport pointer that forwards repo-wide policy to `../AGENTS.md` +- `copilot-commit-message-instructions.md`: supporting commit message guidance for Copilot flows +- `COPILOT_SETUP_VALIDATION.md`: supporting snapshot of the Copilot-oriented workspace setup +- `prompts/`: reusable GitHub Copilot prompt files +- `agents/`: reusable custom agent definitions +- `instructions/`: vendor-style scoped instruction files and references +- `workflows/`: GitHub Actions workflows + +## Governance + +- Repo-wide policy is canonical in `../AGENTS.md`. +- Tool-specific files under `.github/` must not redefine canonical repo policy. +- If a `.github/` asset references global rules, it should point to `../AGENTS.md` and the canonical docs rather than owning those rules itself. diff --git a/.github/copilot-commit-message-instructions.md b/.github/copilot-commit-message-instructions.md index 271c79ce0..770d4c621 100644 --- a/.github/copilot-commit-message-instructions.md +++ b/.github/copilot-commit-message-instructions.md @@ -1,5 +1,8 @@ # Commit Guidelines Prompt +> Doc status: supporting. +> This document preserves Copilot-specific commit guidance. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + You are a commit message generator for a strict Conventional Commits workflow. If the commit message contains vague phrases such as "for clarity", "for specificity", "for better understanding", or similar filler expressions, discard it and generate a new commit message without these phrases. @@ -70,4 +73,3 @@ Always use `rename` as the type if a file or symbol was renamed. - If you encounter shell errors (e.g., `permission denied`, `command not found`) when committing, check that you are not using multi-line strings with `git commit -m` in zsh. Use `printf` with redirect to a temp file and `git commit -F ` instead for multi-line commit messages. - diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7137d3b05..0943f0efc 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,251 +1,17 @@ --- applyTo: "**" --- -# Copilot Instructions (short version) +# Copilot Instructions -# Barrel File Ban +> Doc status: pointer. +> This file is a Copilot transport pointer, not the source of truth. -- Barrel `index.ts` files (files that re-export multiple modules from a directory) are strictly forbidden in this codebase. -- Do not create, update, or use barrel files (e.g., `index.ts` that only re-exports other files). -- Prefer importing directly from the specific file to avoid ambiguous imports and improve tree-shaking. +Canonical repo policy lives in [../AGENTS.md](../AGENTS.md). ---- - -Follow these steps for each interaction: - -1. User Identification: - - Assume you are interacting with `marcuscastelo`. - - The repository name is `macroflows` (https://github.com/marcuscastelo/macroflows). - -## Terminal & Script Usage -- Verify the existence and executability of referenced scripts (for example `.scripts/semver.sh`) before invoking them. If missing, suggest alternatives or prompt for guidance. -- Prefer repository-specific scripts and documented patterns for version reporting and automation. If a particular script is required by a workflow, document its expected location and shell compatibility. - -## Codebase Check & Output Validation -1. Run `npm run copilot:check` in the project root when asked to verify repository checks. -2. After the command finishes, inspect the output of each script in order. Act when: - - The message "COPILOT: All checks passed!" appears in the output, or - - Serious error patterns appear (case-insensitive): `failed`, `at constructor`, `error`, `replace`, or similar. -3. On failures or warnings: - - Re-run the check up to two additional times to rule out transient or flaky failures. - - If failures persist, collect the latest output and run targeted diagnostics (linters, unit tests) to localize the cause. - - If unresolved, open an issue with the collected logs and notify/assign relevant maintainers. -4. Do not block work indefinitely on flaky checks; follow the retry and escalation steps above. - -## Project Context Detection and Solo Project Adaptations - -Before suggesting team-related processes, detect project context: -- Are there multiple active developers? (inspect git history) -- Are stakeholders or formal approval processes documented? - -### Solo Project Adaptations -When operating in a solo-project context (single developer, minimal stakeholders): -- Remove team-specific coordination requirements from suggested workflows. -- Maintain technical quality (tests, monitoring, backups) while simplifying collaboration overhead. -- Replace peer-review steps with systematic self-review and automated checks. -- Convert coordination tasks into automated validations or lightweight checklists. - -### Quality Standards Adaptation -- Maintain technical quality: testing, monitoring, and error handling. -- Prefer automated validation and systematic self-review in solo setups. - -### Documentation Generation for Solo Projects -- Detect project type early and generate context-appropriate documentation and templates. -- Avoid adding team-coordination workflows for solo projects. - -## Reporting and Attribution - -- Use a `reportedBy` metadata field only for outputs intended for downstream processing or automated auditing (for example: structured logs, machine-readable artifacts, PR automation metadata, or audit documents). -- When required for machine-to-machine consumption, `reportedBy` should follow the pattern `.v`. -- For casual conversational replies or interactive assistance, `reportedBy` is not required. -- When included, place `reportedBy` at the top of the machine-readable output in a clear, parseable format. - -Example (machine-to-machine output): - -```markdown -reportedBy: - -### Session Learnings -- ... -``` - -## Refactoring & Automation -- For large refactors, use terminal commands and document what was changed and why (commands used, scope, and verification steps). -- After batch changes, run `npm run check` and follow the Codebase Check & Output Validation guidance above. -- If checks fail repeatedly, follow retry and escalation procedures; do not loop indefinitely until a single string appears. - -## JSDoc -- Required: Add JSDoc to all exported TypeScript types, interfaces, and functions describing purpose, parameters, and return values. -- Internal (non-exported) code may include concise comments when necessary; prefer clear code and tests. -- Use internal comments to explain non-obvious rationale that helps future maintainers. -- Avoid adding JSDoc to purely internal helpers unless it provides clear value. - -## Language -- Use English for all code identifiers, comments, and commit messages to maximize accessibility. -- Localize UI text where appropriate (for example: `pt-BR` for user-facing strings) and document localization locations. - -## Naming & Structure -- Use descriptive, action-oriented names. Avoid vague identifiers. Organize code by module and responsibility. - -## Clean Architecture -- Domain layer: pure logic, no side effects (no UI or observability calls). -- Application layer: orchestrates use cases, catches domain errors, and integrates with UI/observability utilities. - -## Error Handling -- Domain code should throw pure errors only. -- Application code should catch domain errors and use `showError` for user-facing messages and `logging` + `src/modules/observability` for telemetry. -- Attach context to errors using `cause` or a `context` property when available. -- Do not log or throw errors silently; always record structured logs and surface friendly messages when appropriate. - -## Promises -- Do not silently swallow errors with patterns like `.catch(() => {})`. -- If an error is intentionally ignored, document the reason inline and/or send it to centralized logging, for example: - - `.catch(err => { /* intentionally ignored because ... */ })` - - `.catch(logging.capture)` -- Use `void` for fire-and-forget only in event handlers or clearly non-critical effects. Always consider and document failure modes. - -## Formatting & Style -- Use Prettier and ESLint for JS/TS formatting and linting. -- Prefer type aliases over `any` in TypeScript and follow repository linting rules. - -## Imports -- Prefer static imports at the top of modules for clarity and toolchain compatibility. -- Barrel files (directory index re-exports) are forbidden; always import directly from the specific file. -- When a wrapper or an exported helper exists, import that wrapper instead of reaching into internals. -- If the project adopts absolute import aliases (for example `~/`), document the required `tsconfig` / bundler configuration. -- Exceptions for import style (e.g., dependency injection patterns, test utilities, or framework constraints) should be documented and justified. -- Avoid runtime indirection patterns that rely on top-level side effects to defer wiring or module loading. Specifically: - - Do not use `Proxy` objects at module scope as a mechanism to lazily resolve or mutate dependencies. - - Do not mutate or depend on `globalThis` (or other global singletons) from module initialization to perform deferred DI or to hide wiring. - - Do not use `setTimeout`/`setInterval` at the root (module) level to schedule deferred initialization or to "wait" for other modules. - These techniques create implicit global state, non-deterministic module behavior, harder-to-debug startup ordering problems, and brittle tests and bundling results. -- Prefer explicit, deterministic alternatives for deferred initialization and lazy loading: - - Use factory/initializer functions (for example `createApp()` or `initServices()`) that perform wiring in a single, well-documented startup location. - - Use an explicit DI container or providers that are initialized during bootstrap rather than relying on hidden side effects. - - Use dynamic `import()` inside a function when true code-splitting / lazy-loading is required. - - If a proxy-like indirection is necessary, scope it to a function/local context and document why it cannot be expressed via explicit factories. -- Document any justified exception with the rationale, test coverage that demonstrates predictable behavior, and a clear migration path away from implicit global or timer-based wiring. - -Examples: -- Preferred static import: `import { Button } from '~components/ui/Button'` -- When a wrapper is intended: `import { withAuth } from '~lib/wrappers/withAuth'` (import the wrapper, not internals) - -## Context Propagation -- Prefer global signals/utilities for widely shared context (for example, macro context) to avoid deep prop drilling when appropriate. - -## Testing -- Update tests for all behavior changes and run `npm run check` after changes. -- Use the retry and escalation steps when checks are flaky; do not block indefinitely on transient failures. - -## Cleanup After Refactor -- After API or context changes, search for and remove unused props, imports, and signals in affected modules. - -# Additional Enforcement -- Avoid blanket prohibitions that harm maintainability; prefer explained guidance with concrete examples and documented exceptions. -- Use English for code and comments. Localized UI text is permitted where appropriate. -- Prefer small, atomic commits; document recommended commit messages. -- Always update or adjust tests when changing behavior. -- Keep TODOs actionable: convert to issues or review them periodically rather than keeping them permanently in code. - -## Commit Message Output -- Use Conventional Commits as the standard format for commit messages. Provide a short header and an optional body. - -Example: - -```markdown -feat(parser): add support for X - -Add a brief description of the change and reasoning. -``` - -- When producing automated outputs that reference changes, use repository tooling and documented automation fields (for example `#changes`) to surface diffs. - -# Label Usage - -Refer to `docs/labels-usage.md` for full rules. The guidance below is intentionally advisory rather than prescriptive. - -## Quick Reference - -- Prefer at least one main type label: `bug`, `feature`, `refactor`, `task`, `improvement`, `documentation`, `chore`, `epic`, `idea`. -- Add complexity labels when helpful: `complexity-low`, `complexity-medium`, `complexity-high`, `complexity-very-high`. -- Use status/context labels where they add value: `todo :spiral_notepad:`, `blocked`, `needs-investigation`, `needs-design`, `may-return-in-the-future`. -- Add area labels when useful: `ui`, `backend`, `api`, `performance`, `data-consumption`, `accessibility`. -- Use grouping/refinement labels: `refinement`, `epic`. -- Remove or reclassify generic labels like `todo :spiral_notepad:` during triage. -- Avoid duplicate or conflicting labels. - -For the full label table and descriptions, refer to `docs/labels-usage.md`. - -## Search Features (pt-BR/Portuguese) -- All user-facing search features must be both diacritic-insensitive and case-insensitive for pt-BR/Portuguese contexts. - -## Memory Usage - -This project supports the use of persistent agent memories to capture and reuse project-specific context, workflows, and heuristics. Use memories thoughtfully and sparingly: they are intended to speed up repeated tasks and preserve institutional knowledge, not to store transient or sensitive information. - -### Purpose -- Persist important, project-specific guidance or context that helps the agent perform actions consistently (examples: architecture decisions, workflow checklists, common search patterns). -- Capture outcomes of long-running analysis or handoffs that will be reused across sessions. -- Store canonical summaries or indexes to speed discovery (e.g., module map, developer-guidelines, workflow-and-commands). - -### When to Write Memories -- After producing a stable summary or guideline that will be reused (architecture decisions, consolidated dev rules). -- When a repeated operational pattern is discovered (search patterns, command sequences). -- When completing a migration or a non-trivial refactor that others may need to reference later. -- Prior to handing off context to another agent or human to preserve decisions. - -Recommended memory names (examples): -- `project-context-macroflows` -- `developer-guidelines` -- `workflow-and-commands` -- `module-map` -- `lessons-learned` - -### When to Read Memories -- Read a memory only if it is clearly relevant to the task at hand. -- Avoid re-reading the same memory multiple times in the same conversation. -- Prefer targeted memory reads (specific memory names) over broad searches. - -### Memory Format & Naming -- Store memories in Markdown for readability. -- Use clear, descriptive memory names (snake-case or kebab-case recommended). -- Include a short metadata header in the memory body when useful (e.g., `lastUpdated`, `reportedBy` for machine-readable outputs). -- Do NOT store secrets, credentials, or PII in memories. - -Example memory structure (Markdown): -- Title -- Purpose -- Short summary -- Relevant links/paths in the repo -- Actions or next steps -- lastUpdated: YYYY-MM-DD -- reportedBy: (optional for machine artifacts) - -### Editing & Deleting Memories -- Use precise edits when updating memories (preserve historical notes if relevant). -- If a memory becomes obsolete, delete it rather than leaving outdated content. -- If only part of a memory is obsolete, update that section and add a changelog entry with `lastUpdated`. - -### Security & Privacy -- Never write API keys, secrets, or personal data to memories. -- Sensitive or restricted information must be referenced indirectly (e.g., “see secure vault”) rather than stored in memory. -- If an accidental secret is written to a memory, delete the memory immediately and follow repository security procedures. - -### Usage Patterns & Examples -- Before performing cross-module refactors, load `developer-guidelines` and `module-map` to verify naming and layering rules. -- For repetitive searches (e.g., "recipe edit" TODOs), store the search regex in a memory to avoid re-deriving it each session. -- Use a `lessons-learned` memory to capture post-mortems; convert actionable items into GitHub issues. - -### Best Practices -- Keep memories small and focused; prefer several specific memories over a single large blob. -- Use consistent naming so memories are discoverable. -- Prefer writable canonical docs in the repository for rules that must be visible to humans; memories are a complementary convenience for the agent. -- When automating workflows or commands, reference memories in the automation steps (e.g., load `workflow-and-commands` before running batch validation). -- Treat memories as first-class artifacts: update them when processes change. +If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. -### Integration with Agent Workflows -- Load relevant memories during agent handoffs to preserve context (e.g., architecture rules, active migrations, quality gate expectations). -- When producing machine-readable outputs intended for automation or audit, include `reportedBy: ` at the top of the artifact. -- Use memories to reduce redundant discovery steps (e.g., mapping TODOs to known issue areas), but validate memory contents against the repository when precision matters. +## Copilot-specific notes -By following these guidelines, memories become a reliable accelerator for consistent, high-quality assistance without compromising security or clarity. +- Keep the `applyTo` frontmatter so Copilot discovers this file automatically. +- Copilot prompt and agent assets under `.github/prompts/` and `.github/agents/` remain tool-specific supporting material. +- Use the canonical workflow and command defaults from `AGENTS.md`. diff --git a/.github/prompts/code-review.prompt.md b/.github/prompts/code-review.prompt.md index 9cdc0c8cc..94ef461e5 100644 --- a/.github/prompts/code-review.prompt.md +++ b/.github/prompts/code-review.prompt.md @@ -36,7 +36,7 @@ tools: ['changes', 'search/codebase', 'edit/editFiles', 'extensions', 'fetch', ' ## Global Rules and Traceability - Ensure every review and output includes a `reportedBy` field for traceability. -- Reference and follow all global rules and checklists in the main project documentation (see docs/COPILOT_SHORT_GUIDE.md or equivalent global instructions). +- Reference and follow all repo-wide rules and checklists in [AGENTS.md](../../AGENTS.md) and the canonical docs. You are: github-copilot.v1/code-review-actionable -reportedBy: github-copilot.v1/code-review-actionable \ No newline at end of file +reportedBy: github-copilot.v1/code-review-actionable diff --git a/.github/prompts/issues-worktree.prompt.md b/.github/prompts/issues-worktree.prompt.md index 4782f07e6..f85327b29 100644 --- a/.github/prompts/issues-worktree.prompt.md +++ b/.github/prompts/issues-worktree.prompt.md @@ -51,9 +51,9 @@ This agent receives a list of GitHub issue numbers, fetches their content using - [Refine GitHub Issue Prompt](refine-github-issue.prompt.md) - [Copilot Customization Instructions](../instructions/copilot/copilot-customization.instructions.md) - [Labels Usage Guide](../../docs/labels-usage.md) -- [copilot-instructions.md](../copilot-instructions.md) +- [AGENTS.md](../../AGENTS.md) - See `docs/` for required issue templates and structure. -- See [./refine-prompt.prompt.md](./refine-prompt.prompt.md) and [copilot-instructions.md](../copilot-instructions.md) for global rules and checklists. +- See [./refine-prompt.prompt.md](./refine-prompt.prompt.md) and [AGENTS.md](../../AGENTS.md) for repo-wide rules and checklists. --- diff --git a/.github/prompts/pr-reviews.prompt.md b/.github/prompts/pr-reviews.prompt.md index 366bb2171..ef5419dd7 100644 --- a/.github/prompts/pr-reviews.prompt.md +++ b/.github/prompts/pr-reviews.prompt.md @@ -11,9 +11,9 @@ tools: ['changes', 'codebase', 'editFiles', 'extensions', 'fetch', 'findTestFile You are an agent responsible for processing pull request (PR) reviews using the `activePullRequest` tool. Your workflow is as follows: 0. **Pre-Implementation Check** - - Before making any changes or presenting review summaries, always run `npm run copilot:check` and the full set of custom output validation scripts as described in the user instructions. + - Before making any changes or presenting review summaries, always run `pnpm check` and the full set of custom output validation steps described by the repo's canonical docs. - If any errors or warnings are reported, automatically analyze and correct the issues in the codebase. - - Repeat the check/fix cycle until the message "COPILOT: All checks passed!" appears. + - Repeat the check/fix cycle until the repository quality gate passes. 1. **Fetch All Reviews** Use the `activePullRequest` and `gh` (CLI) tools to retrieve all reviews and comments for the active PR. @@ -35,9 +35,9 @@ You are an agent responsible for processing pull request (PR) reviews using the - For each suggestion approved by the user (or all, if blanket approval is given), implement the change in the codebase. - **For each suggestion:** - Make the code change. - - Run `npm run copilot:check` and all custom output validation scripts. + - Run `pnpm check` and any additional validation required by the affected area. - If any errors or warnings are reported, automatically analyze and correct the issues in the codebase, then re-run the checks. - - Repeat the fix/check cycle until the message "COPILOT: All checks passed!" appears. + - Repeat the fix/check cycle until the repository quality gate passes. - Once all checks pass, stage the modified file(s) (`git add`). - Only then generate and run the commit for that suggestion. - **You must implement every approved suggestion unless it is impossible or explicitly deferred.** @@ -48,14 +48,14 @@ You are an agent responsible for processing pull request (PR) reviews using the - Only proceed to completion after this explicit reporting. 5. **Code Quality Check Loop** - - After any code change, always run `npm run copilot:check` and the full set of custom output validation scripts as described in the user instructions. + - After any code change, always run `pnpm check` and the full set of applicable validation steps described by the canonical docs. - If any errors or warnings are reported, automatically analyze and correct the issues in the codebase. - - Repeat the check/fix cycle until the message "COPILOT: All checks passed!" appears. + - Repeat the check/fix cycle until the repository quality gate passes. - **Never prompt the user for input, approval, or review until all checks pass.** 6. **Post-Implementation Check** - - After all implementation and before prompting the user for input, always run `npm run copilot:check` and the full set of custom output validation scripts again. - - Only proceed to user approval if the message "COPILOT: All checks passed!" appears. + - After all implementation and before prompting the user for input, always run `pnpm check` and any additional validation required by the affected area again. + - Only proceed to user approval if the repository quality gate passes. 7. **User Approval** - Only after all checks pass, present the summary table and ask the user to approve the changes to be implemented, or to specify which suggestions to accept or reject. @@ -66,7 +66,7 @@ You are an agent responsible for processing pull request (PR) reviews using the - If any suggestion was not implemented, you must clearly list it and the reason before reporting completion. 9. **References** - - Follow best practices from [copilot-instructions.md](../copilot-instructions.md) and [copilot-customization.instructions.md](../copilot-instructions.md). + - Follow best practices from [AGENTS.md](../../AGENTS.md) and [Copilot Customization Instructions](../instructions/copilot/copilot-customization.instructions.md). ## Output diff --git a/.github/prompts/refactor.prompt.md b/.github/prompts/refactor.prompt.md index 9a4f23130..4cd0b8f16 100644 --- a/.github/prompts/refactor.prompt.md +++ b/.github/prompts/refactor.prompt.md @@ -39,7 +39,7 @@ You are a programming assistant specialized in SolidJS, Tailwind, daisyUI, and C - **Promises:** - Use `void` only in non-critical handlers/events. - **Testing:** - - Always run `npm run copilot:check` and proceed only if “COPILOT: All checks passed!”. + - Always run `pnpm check` and proceed only if the repository quality gate passes. - **Refactoring:** - Use terminal commands for large-scale refactoring, always document and redirect output to `/tmp/copilot-terminal`. - **Commits:** @@ -111,6 +111,6 @@ refactor(weight): optimize period grouping in WeightEvolution to O(n) > Follow all the rules above for any task, refactoring, or implementation in this workspace. Always modularize, document, test, and validate as described. Never break conventions or skip validation steps. Continue from this context, keeping all preferences and learnings above. If the user asks to resume, use this prompt as a base to ensure continuity and consistency in project support. -- All code comments, including minor or nitpick comments, must be in English. Reviewers must flag and suggest converting any non-English comments to English. See [copilot-instructions.md](../copilot-instructions.md) for global rules and solo project adaptations. +- All code comments, including minor or nitpick comments, must be in English. Reviewers must flag and suggest converting any non-English comments to English. See [AGENTS.md](../../AGENTS.md) for repo-wide rules and defaults. -reportedBy: github-copilot.v1/refactor \ No newline at end of file +reportedBy: github-copilot.v1/refactor diff --git a/.github/prompts/refine-github-issue.prompt.md b/.github/prompts/refine-github-issue.prompt.md index 506a33579..40c383e44 100644 --- a/.github/prompts/refine-github-issue.prompt.md +++ b/.github/prompts/refine-github-issue.prompt.md @@ -48,14 +48,14 @@ This agent receives a GitHub issue (by number or content) as input and guides th - This ensures robust handling of multi-line Markdown and avoids shell quoting issues. - Once confirmed, handle label changes directly (not just suggest them) and update both the issue content and labels in a single workflow, unless the user requests otherwise. - Handle errors from the GitHub CLI (e.g., missing files) by creating the necessary files automatically before retrying the command. - - Include a `reportedBy` metadata field at the top for traceability. See [copilot-instructions.md](../copilot-instructions.md) for global reporting and attribution rules. + - Include a `reportedBy` metadata field at the top for traceability. See [AGENTS.md](../../AGENTS.md) for repo-wide reporting and attribution defaults. ## References - [Issue Templates](../../docs/) - [Copilot Customization Instructions](../instructions/copilot/copilot-customization.instructions.md) - [Labels Usage Guide](../../docs/labels-usage.md) -- [copilot-instructions.md](../copilot-instructions.md) +- [AGENTS.md](../../AGENTS.md) ## Example Workflow diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..5bcf92ab0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,69 @@ +# AGENTS.md + +> Doc status: canonical. +> This is the canonical repo-wide agent entrypoint for Macroflows. + +Macroflows is a SolidJS + TypeScript nutrition tracking platform backed by Supabase. This file is intentionally short: it defines repo-wide policy, the read-first path, command defaults, and where deeper canonical detail lives. + +## Policy + +### Read-first order + +Read these files in order before making non-trivial changes: + +1. `AGENTS.md` +2. `docs/BOUNDARIES.md` +3. `docs/ARCHITECTURE.md` +4. `docs/DOCS_GOVERNANCE.md` +5. Any directly relevant ADR under `docs/adr/` + +Only canonical docs belong in read-first lists. + +### Precedence + +- `AGENTS.md` is the canonical repo-wide agent entrypoint. +- More specific canonical docs win inside their own scope: + - `docs/BOUNDARIES.md` for dependency and ownership rules + - `docs/ARCHITECTURE.md` for current structure and system map + - `docs/DOCS_GOVERNANCE.md` for lifecycle, ownership, and update workflow +- ADRs record decision history. They do not silently override canon. If an ADR changes current policy, the same change must update the canonical docs in the same PR or commit. +- `CLAUDE.md`, `GEMINI.md`, `.github/copilot-instructions.md`, audits, migration plans, and tool-specific prompt assets are not canonical. + +### Command defaults + +- Use `pnpm`. +- Use `pnpm check` as the default repository quality gate. +- Use `pnpm build`, `pnpm test`, and `pnpm lint` for targeted validation when needed. + +### Repo-wide defaults + +- Prefer clear, explicit code over scaffolding-heavy abstractions. +- Domain code uses standard `Error` with a descriptive message and optional `cause`. +- Application and UI layers handle user-facing feedback with `showError` and telemetry with `logging`. +- Do not add new feature-flag, rollout, rollback, or fallback scaffolding by default. +- Preserve existing compatibility code that still supports active model or data transitions. +- Describe the repo honestly: `src/modules/diet/*` is a large transitional macro-context with internal subdomains, not a completed bounded-context split. + +## Workflow + +Use the smallest canonical update that matches the change: + +- Small repo-wide agent rule or workflow default: update `AGENTS.md` +- Dependency or layer rule change: update `docs/BOUNDARIES.md` +- Current structure or system-map change: update `docs/ARCHITECTURE.md` +- Cross-cutting or long-lived decision: add or supersede an ADR and update canon in the same change +- Audits, investigations, migration notes, and implementation plans: supporting or archived docs only + +The repo owner for canonical docs is the repository maintainer. Any change to `AGENTS.md`, `docs/BOUNDARIES.md`, `docs/ARCHITECTURE.md`, `docs/DOCS_GOVERNANCE.md`, or files under `docs/adr/` must update related canonical docs in the same PR or commit. + +## References + +Canonical docs: + +- `docs/README.md` +- `docs/BOUNDARIES.md` +- `docs/ARCHITECTURE.md` +- `docs/DOCS_GOVERNANCE.md` +- `docs/adr/README.md` + +Supporting docs remain available for examples, audits, and migration context, but they are not a source of truth. diff --git a/CLAUDE.md b/CLAUDE.md index 279c8bdb7..bbd42d63e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,191 +1,14 @@ # CLAUDE.md -Macroflows nutrition tracking platform: SolidJS, TypeScript, Supabase. Domain-driven design with layered architecture. Solo project by marcuscastelo. +> Doc status: pointer. +> This file is a Claude-specific pointer, not the source of truth. -## Frontend Simplicity Principles - CRITICAL +Canonical repo policy lives in [AGENTS.md](./AGENTS.md). -**🚨 FRONTEND APP - NOT A LIBRARY** +If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. -**Never Add:** -- Custom error classes (use `Error()` + Zod) -- Abstract base classes (unless 3+ implementations) -- Domain-specific exceptions (use descriptive messages) -- Complex hierarchies (prefer composition) -- Enterprise patterns (solo project - keep simple) +## Claude-specific notes -**Before Adding Abstractions:** -- Will this have 3+ implementations? -- Does this solve an actual problem? -- Is platform functionality insufficient? -- Will this reduce total code lines? - -**Golden Rule:** Name 3 concrete implementations or don't create it. - -## Speed Over Complexity - -**Never Add:** -- Backward compatibility (Vercel rollback exists) -- Feature flags (implement directly) -- A/B testing infrastructure (manual testing sufficient) -- Fallback mechanisms (trust implementation) -- Migration strategies (direct replacement) -- Enterprise rollout plans (solo project) - -**Decision Framework:** -- Can we build directly without scaffolding? -- Are we using database/framework optimally? -- Can we delete complexity instead of adding? -- Should this live in PostgreSQL vs TypeScript? - -**Logic Placement (Preferred Order):** -1. PostgreSQL functions (search, data processing) -2. Domain layer (business logic, validations) -3. Application layer (SolidJS orchestration, error handling) -4. Infrastructure layer (external APIs) - -**Example:** -```typescript -// ❌ Complex: Client normalization + server merging + DB queries -// ✅ Simple: PostgreSQL function + single RPC call -``` - -**Implementation Patterns:** -- Complex logic → PostgreSQL functions -- Orchestration → TypeScript application layer -- UI state → SolidJS signals/effects -- Validation → Zod schemas - -**Testing:** -```typescript -// ❌ Testing TypeScript guarantees -// ✅ Testing behavior that matters -test('calls correct search function', () => { - expect(deps.fetchFoodsByName).toHaveBeenCalledWith(search, { limit: 50 }) -}) -``` - -**Complexity Checklist:** -- Does platform already solve this? -- Solving real problem vs theoretical? -- Will this reduce total lines? - -**Choose Simple:** -- Single RPC call vs client orchestration -- Standard Error() vs custom hierarchies -- PostgreSQL vs client logic -- Zod vs manual validation - -## Commands & Setup - -**Environment:** Use pnpm (10.12.1+) - -**🚨 CRITICAL: Always run `pnpm check` before declaring complete** - -**Commands:** -- `pnpm check` - MANDATORY quality gate (lint, type-check, test) -- `pnpm fix` - Auto-fix ESLint issues -- `pnpm build/test/lint` - Individual checks - -**Claude Commands:** See `.claude/commands/` directory - -**Workflow:** -- `/fix` - Automated checks and fixes -- `/commit` - Generate conventional commits -- `/pull-request` - Create PRs -- `/implement ` - Full issue implementation - -## Architecture - -**3-Layer Domain-Driven Design:** - -**Domain** (`modules/*/domain/`): -- Pure business logic, Zod schemas -- Never import errorHandler or side effects -- Throw standard `Error()` with context - -**Application** (`modules/*/application/`): -- SolidJS orchestration, error handling -- Must catch errors and call `errorHandler.apiError` -- Global reactive state with signals/effects - -**Infrastructure** (`modules/*/infrastructure/`): -- Supabase repositories, external APIs -- Only layer allowed `any` types for external APIs - -## Error Handling - -**Domain Layer:** Standard `Error()` + Zod validation -**Application Layer:** Always catch and call `errorHandler.apiError` - -**Required Context:** `component`, `operation`, `additionalData` - -**Avoid:** Custom error classes, domain-specific types, instanceof checks - -## Component Patterns - -**Fire-and-Forget:** Use `void` only in event handlers with application-layer error handling - -**Compound Components:** `Modal.Header = ModalHeader` - -**Global State:** Prefer signals over prop drilling, use context for scoped state - -## Testing - -**Trust TypeScript:** Don't test what compiler guarantees -**Focus on Behavior:** Test business logic, calculations, integrations -**Avoid Redundancy:** No type validation tests if TypeScript enforces - -**Setup:** Vitest + jsdom, tests in `tests/` folder, update when code changes - -## Code Style - -**Imports:** Always absolute with `~/` prefix, no barrel files, static only -**Language:** English code/comments, Portuguese UI text allowed -**Types:** Never `any` (except infrastructure), prefer type aliases, use Zod -**Naming:** Descriptive action-based names, avoid generic utils.ts -**CSS:** Always use `cn()` for Tailwind class merging - -## File Organization - -``` -src/ -├── modules/ # Domain modules (diet, user, etc.) -│ └── domain/application/infrastructure/ui/tests/ -├── sections/ # Page-level UI (common/, day-diet/, etc.) -├── routes/ # SolidJS pages and API endpoints -├── shared/ # Cross-cutting utilities -└── assets/ # Static assets -``` - -**Rules:** Domain modules follow clean architecture, sections for UI, shared utilities framework-agnostic - -## Workflow Standards - -**Pre-Commit:** Always run `pnpm check` - MUST PASS before completion -**Refactoring:** Measure with `git diff --stat`, preserve functionality, incremental changes -**Commits:** Conventional format, English only, NEVER include "Generated with Claude Code" -**JSDoc:** Update for exported functions only, English, remove outdated -**TODOs:** Never remove TODO comments -**Console:** BANNED - use `devConsole`, `errorHandler.apiError`, `logToBreadcrumb` -**Solo Project:** No team coordination, focus on technical validation - -## Tech Stack - -**Frontend:** SolidJS, TypeScript, TailwindCSS, DaisyUI -**Backend:** Supabase (PostgreSQL + Realtime) -**Validation:** Zod schemas -**Testing:** Vitest + jsdom -**Build:** Vinxi (Vite-based) - -**Key Dependencies:** @solidjs/start, @supabase/supabase-js, solid-toast, html5-qrcode, dayjs, axios - -## Search Features - -**Portuguese Support:** All search must be diacritic-insensitive and case-insensitive -**Use:** `removeDiacritics` utility for text normalization - -## Memory Bank - -- NEVER destructure `props` (breaks reactivity) -- "Fix tests" = adjust test structure only, not production code -- DELETE moved files completely after content transfer \ No newline at end of file +- Claude transport and permissions for this repo live in `.claude/settings.json`. +- Claude command assets remain under `.claude/commands/`. +- Use the canonical workflow and command defaults from `AGENTS.md`. diff --git a/DI-migration-plan.md b/DI-migration-plan.md index f056188f3..9ec1c235a 100644 --- a/DI-migration-plan.md +++ b/DI-migration-plan.md @@ -1,5 +1,8 @@ # DI Migration Plan — macroflows +> Doc status: supporting. +> This document preserves migration history and implementation context. It is not a source of truth; use `./AGENTS.md` and the canonical docs instead. + Status: Draft (complete, actionable plan for externalizing DI across `src/**/application/**`) This document contains a step-by-step migration plan, batch list with files, templates, commands, commit/PR guidance, verification checklist, and troubleshooting notes. Save this file and use it as your source of truth when you reset the conversation and implement the changes. diff --git a/GEMINI.md b/GEMINI.md index e41c3272e..71e536011 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -1,223 +1,19 @@ --- applyTo: "**" --- -# Gemini Instructions for Macroflows - -At the end of every message, show . - -## 1. Project Overview & Context - -- **Project Name:** Macroflows -- **Description:** A nutrition tracking platform with a focus on strong typing, reactive UI, and modular domain-driven design. -- **Author:** This is a solo project by `marcuscastelo`. -- **Adaptation:** All suggestions must be adapted for a solo developer. This means removing team coordination, stakeholder approval, and peer review processes. Focus on technical validation and systematic self-review. Maintain all technical quality standards. - -## 2. Core Technologies - -- **Frontend:** SolidJS, TypeScript, TailwindCSS, DaisyUI -- **Backend:** Supabase (PostgreSQL + Realtime), Vercel -- **Validation:** Zod (for runtime validation and type inference) -- **Testing:** Vitest with jsdom -- **Package Manager:** pnpm -- **Build Tool:** Vinxi (Vite-based) - -## 3. 🚨 CRITICAL: Development Workflow & Quality Gates - -### 3.1. The Golden Rule: `pnpm check` -- **MANDATORY:** Before declaring any task, fix, or feature complete, you **MUST** run `pnpm check`. -- This command runs linting, type-checking, and all tests. It is the single source of truth for codebase health. -- **NEVER** commit code that fails `pnpm check`. - -### 3.2. Workflow Steps -1. **Implement Changes:** Write or modify the code as requested. -2. **Run Quality Gate:** Execute `pnpm check`. -3. **Verify:** Ensure all checks pass with zero errors (TypeScript, ESLint, Tests). -4. **Commit:** Only after all checks pass, proceed to commit the changes. - -### 3.3. Essential Commands -- `pnpm check`: **MANDATORY** quality gate. -- `pnpm fix`: Auto-fix ESLint issues. -- `pnpm build`: Create a production build. -- `pnpm test`: Run all tests. -- `pnpm lint`: Run ESLint. -- `.scripts/semver.sh`: The preferred method for application version reporting. - -## 4. 🚀 Rapid Implementation Guidelines - CRITICAL - -**SPEED COMES FROM SAYING NO TO UNNECESSARY COMPLEXITY** - -### 4.1. Implementation Velocity Principles - -**Never Add These Unless Absolutely Essential:** -- **Backward Compatibility**: Frontend is versioned - Vercel rollback solves problems -- **Feature Flags**: Just implement the feature directly -- **A/B Testing Infrastructure**: Manual testing is sufficient for most cases -- **Fallback Mechanisms**: Trust your implementation and monitoring -- **Migration Strategies**: Direct replacement with proper testing -- **Enterprise Rollout Plans**: This is a solo project with simple deployment - -**Speed-First Decision Framework:** -- [ ] **Direct implementation**: Can we just build the feature without scaffolding? -- [ ] **Platform leverage**: Are we using database/framework strengths optimally? -- [ ] **Delete over add**: Can we remove complexity instead of adding abstraction? -- [ ] **Server-side logic**: Should this logic live in PostgreSQL instead of TypeScript? -- [ ] **Testing necessity**: Does this need a test or does TypeScript/DB already guarantee it? - -### 4.2. Logic Placement Hierarchy (Most to Least Preferred) - -1. **PostgreSQL Functions (RPC)**: For search, data processing, complex queries -2. **Domain Layer**: Pure business logic, validations, calculations -3. **Application Layer**: SolidJS orchestration, error handling, UI state -4. **Infrastructure Layer**: External API calls, data transformation - -**Example Decision Tree:** -```typescript -// ❌ Complex: Spread across layers -// Client: word splitting + normalization -// Server: multiple API calls + merging -// Database: simple ILIKE queries - -// ✅ Simple: Centralized in optimal layer -// PostgreSQL: All search logic with scoring -// Client: Single RPC call + mapping -``` +# GEMINI.md -### 4.3. Rapid Implementation Patterns - -**Database-First for Complex Logic:** -- Text search → PostgreSQL functions with scoring -- Data aggregations → SQL with CTEs -- Complex filtering → Server-side functions -- Real-time updates → Supabase subscriptions - -**TypeScript for Orchestration Only:** -- Error handling and user feedback -- State management and reactivity -- Domain object mapping and validation -- UI component coordination - -**Testing Reality Check:** -```typescript -// ❌ Over-testing: What TypeScript already guarantees -test('should have correct type structure', () => { - expect(typeof food.name).toBe('string') // TypeScript already ensures this -}) - -// ✅ Behavioral testing: What actually matters -test('should call correct search function for tab', () => { - expect(deps.fetchFoodsByName).toHaveBeenCalledWith(search, { limit: 50 }) -}) -``` +> Doc status: pointer. +> This file is a Gemini-specific pointer, not the source of truth. -### 4.4. Implementation Speed Checklist - -**Before adding any complexity, ask:** -- [ ] **Platform sufficiency**: Does PostgreSQL/Supabase/SolidJS already solve this? -- [ ] **Real user problem**: Are we solving an actual issue or theoretical edge case? -- [ ] **Deployment reality**: Is Vercel rollback + monitoring sufficient safety net? -- [ ] **Maintenance cost**: Will this make future changes harder or easier? -- [ ] **Line count impact**: Does this reduce or increase total codebase size? - -**When to choose simple over "robust":** -- ✅ **Single RPC call** vs elaborate client-side orchestration -- ✅ **Standard Error()** vs custom error hierarchies -- ✅ **Direct implementation** vs abstraction layers -- ✅ **PostgreSQL functions** vs client-side complex logic -- ✅ **Zod validation** vs manual type checking - -### 4.5. Database Logic Advantages - -**Why prefer PostgreSQL functions:** -- **Performance**: Processing happens close to data -- **Concurrency**: Database handles concurrent requests optimally -- **Consistency**: Single source of truth for complex operations -- **Optimization**: Query planner + indexes automatically optimize -- **Simplicity**: TypeScript becomes thin orchestration layer - -**Example - Search Implementation:** -```sql --- ✅ All logic in database function -CREATE FUNCTION search_foods_with_scoring(p_search_term text, p_limit integer) --- Complex scoring, fuzzy matching, normalization all server-side -``` +Canonical repo policy lives in [AGENTS.md](./AGENTS.md). -```typescript -// ✅ Simple client call -const result = await supabase.rpc('search_foods_with_scoring', { - p_search_term: name, - p_limit: params.limit ?? 50 -}) -``` +If anything in this file conflicts with `AGENTS.md` or the canonical docs, the canonical docs win. + +## Gemini-specific notes -### 4.6. Key Success Metrics - -**Implementation completed in ~1 hour instead of potential days/weeks** -- ✅ **Rejected complexity**: No backward compatibility, feature flags, fallbacks -- ✅ **Leveraged platform**: PostgreSQL for search logic optimization -- ✅ **Deleted code**: Removed 26 lines of word separation logic -- ✅ **Trusted tools**: TypeScript compilation + Vercel deployment patterns -- ✅ **Focused testing**: Only behavioral tests, not redundant type validation - -**Final Reality Check:** -- **Frontend apps are not distributed systems** - avoid over-engineering -- **Vercel rollback > elaborate fallback mechanisms** - trust your deployment -- **PostgreSQL > complex TypeScript** for data processing -- **Delete complexity > add abstractions** - prefer subtraction -- **Good enough > perfect** - solve real user problems quickly - -## 5. Architecture & Design Principles - -### 5.1. Clean Architecture (3 Layers) -The codebase follows a strict 3-layer architecture. Adherence to these boundaries is critical. - -1. **Domain Layer** (`modules/*/domain/`) - - Contains pure business logic, entities, types (Zod schemas), and repository interfaces. - - **MUST NOT** have any dependencies on external frameworks or libraries (like SolidJS or Supabase). - - **MUST NOT** contain any side-effects (e.g., API calls, logging, toasts). - - **MUST ONLY** throw pure, custom domain errors. - - Use `__type` discriminators for type safety in entities. - -2. **Application Layer** (`modules/*/application/`) - - Orchestrates the domain logic. Contains SolidJS resources, signals, and application-specific logic. - - **MUST** catch errors from the Domain layer. - - **MUST** use `showError` for user feedback (toasts) and `logging` for telemetry/observability. - - Manages all side-effects and user feedback (toasts, notifications). - -3. **Infrastructure Layer** (`modules/*/infrastructure/`) - - Implements the repository interfaces defined in the Domain layer. - - Contains all external integrations, such as Supabase client code and Data Access Objects (DAOs). - - This is the **ONLY** layer where `any` might be permissible, strictly for interfacing with external, untyped APIs. - -### 6.2. Dependency Injection (DI) Pattern -- The project uses an explicit, manual Dependency Injection pattern. -- **Orchestration functions** (business logic) in the Application Layer **MUST NOT** import dependencies (repositories, fetchers) directly. -- Instead, these dependencies **MUST** be passed as arguments to the function. -- This decouples application logic from infrastructure, making it highly testable. -- **Example:** A `fetchTemplatesByTabLogic` function should receive a `deps` object containing all necessary fetchers (`fetchUserRecipes`, `fetchFoods`, etc.) as a parameter. - -### 6.3. File & Module Structure -- `src/modules//`: Houses the three architecture layers for a specific domain. - - `tests/`: All tests for a module **MUST** be placed in this folder. This ensures consistent organization and discoverability of tests. -- `src/sections//`: Contains page-level UI components, organized by feature. -- `src/shared/`: Cross-cutting concerns (error handling, configs, pure utilities). -- `src/routes/`: SolidJS router pages and API endpoints. - -## 6. Critical Code Style & Patterns - -### 6.1. Imports: The Three Rules -1. **Absolute Imports ONLY:** Always use absolute paths with the `~/` prefix. - - ✅ `import { MyType } from '~/modules/user/domain/user';` - - ❌ `import { MyType } from '../../user/domain/user';` -2. **Barrel Files (`index.ts`) are BANNED:** All imports must point directly to the file where the entity is defined. Do not create or use `index.ts` files that only re-export from other files. -3. **Static Imports ONLY:** All imports must be static and at the top of the file. Dynamic `import()` is forbidden. - -### 6.2. Language & Naming -- **Language:** All code, comments, JSDoc, and commit messages **MUST** be in **English**. UI text visible to the user may be in Portuguese (pt-BR). -- **Naming:** Use descriptive, specific, action-based names. Avoid generic names. - - ✅ **Good:** `isRecipedGroupUpToDate()`, `convertToGroups()`, `ItemGroupEditModal.tsx`, `macroOverflow.ts` - - ❌ **Bad:** `checkGroup()`, `convert()`, `GroupModal.tsx`, `utils.ts` - -### 6.3. Type Safety & Formatting +- Gemini transport and MCP configuration for this repo live in `.gemini/settings.json`. +- Use the canonical workflow and command defaults from `AGENTS.md`. - **NO `any`:** The use of `any`, `as any`, or `@ts-ignore` is strictly forbidden outside the Infrastructure layer. This ensures strong type safety and reduces the need for redundant runtime type checks in tests. - **`type` over `interface`:** Always use type aliases for defining data shapes. - **Readonly:** Prefer `readonly Item[]` over `Item[]` for immutability. diff --git a/README.md b/README.md index f878b2200..d1ecf9966 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,14 @@ Macroflows is a nutrition tracking system focused on strong typing, reactive UI, For now, it is focused on being a personal project to track my own nutrition, but maybe in the future it will be a SaaS product. +## Project Docs + +- Canonical repo-wide agent entrypoint: [AGENTS.md](./AGENTS.md) +- Canonical architecture map: [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) +- Canonical dependency and ownership rules: [docs/BOUNDARIES.md](./docs/BOUNDARIES.md) +- Canonical docs governance: [docs/DOCS_GOVERNANCE.md](./docs/DOCS_GOVERNANCE.md) +- ADR index: [docs/adr/README.md](./docs/adr/README.md) + --- ## Features diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 000000000..6b30f852d --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,84 @@ +# Architecture + +> Doc status: canonical. +> This file describes the current Macroflows architecture as it exists today. + +## Overview + +Macroflows follows a layered, module-oriented architecture centered on `src/modules/*`, with route and feature composition living outside the modules. The repo shows strong clean-architecture intent, but some areas are still transitional and should be described as such rather than presented as already fully normalized. + +## Current Structure + +### `src/modules/*` + +Business-facing modules live under `src/modules/*`. Many follow a layered shape with `domain`, `application`, `infrastructure`, `ui`, and `tests`. + +Current top-level modules include: + +- `auth` +- `clipboard` +- `diet` +- `import-export` +- `measure` +- `observability` +- `profile` +- `recent-food` +- `search` +- `template-search` +- `theme` +- `toast` +- `user` +- `weight` + +### `src/modules/diet/*` + +`src/modules/diet/*` is the largest transitional macro-context in the repo. It contains internal subdomains such as day-diet, food, item, macro-profile, meal, recipe, and template flows. + +This area should be treated as a real structural hotspot: + +- it is modular enough to reason about +- it is not yet a completed bounded-context split +- new docs should describe it honestly as a transition zone rather than flattening it into a single clean abstraction + +### `src/sections/*` + +`src/sections/*` contains page-level and feature-level composition. It is the main place where UI regions, cross-module presentation flows, and route-facing composition live. + +### `src/shared/*` + +`src/shared/*` contains technical cross-cutting code such as utilities, modal infrastructure, Supabase integration helpers, testing helpers, and framework-specific helpers. It is not the home for canonical nutrition semantics or feature-specific business truth. + +### `src/routes/*` + +`src/routes/*` owns route entrypoints and API endpoints. Routes compose application flows and page/feature sections instead of becoming a second business-logic layer. + +### `src/di/*` + +`src/di/*` is the explicit wiring and composition root. It exists to assemble defaults and dependencies, not to define business rules. + +## Layer Model + +For modules that follow the layered pattern: + +- `domain`: business rules, schemas, and contracts +- `application`: orchestration, use cases, and side-effect coordination +- `infrastructure`: external systems, repositories, gateways, and reactive stores +- `ui`: module-local presentation components +- `tests`: validation of module behavior + +Some modules are stronger on this pattern than others. Canonical docs should treat this as the repo standard while acknowledging that some modules remain less mature or less consistently split. + +## Current Architectural Themes + +- Strong emphasis on TypeScript and Zod for safety +- Preference for module-oriented organization over generic shared abstractions +- Growing use of explicit DI and composition roots +- Ongoing tension between legacy compatibility work and simplification goals +- A deliberate need to keep “current truth” separate from audits, migration plans, and historical documents + +## See Also + +- `../AGENTS.md` +- `./BOUNDARIES.md` +- `./DOCS_GOVERNANCE.md` +- `./adr/README.md` diff --git a/docs/ARCHITECTURE_AUDIT.md b/docs/ARCHITECTURE_AUDIT.md index 153aec69f..334a626f6 100644 --- a/docs/ARCHITECTURE_AUDIT.md +++ b/docs/ARCHITECTURE_AUDIT.md @@ -1,5 +1,8 @@ # Architecture Audit – Summary +> Doc status: supporting. +> This document preserves audit history and improvement notes. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-07-08_ This document provides a high-level overview of the current state of the codebase architecture, focusing on Domain-Driven Design (DDD), modularity, and separation of concerns. For detailed findings and recommendations, see the linked area-specific audits below. diff --git a/docs/ARCHITECTURE_GUIDE.md b/docs/ARCHITECTURE_GUIDE.md index f007d9aab..11c44efd3 100644 --- a/docs/ARCHITECTURE_GUIDE.md +++ b/docs/ARCHITECTURE_GUIDE.md @@ -1,5 +1,8 @@ # 🧭 SolidJS Frontend Architecture Guide +> Doc status: supporting. +> This document preserves useful examples and historical guidance. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + This guide defines the project's standard architecture to ensure consistency, scalability, and maintainability. Follow the sections below to understand how to structure, name, and build each part of the application. --- @@ -595,4 +598,4 @@ export const templates = createResource( - **Decoupling:** Application logic is not tied to infrastructure details. - **Clarity:** Function signatures make dependencies explicit. ---- \ No newline at end of file +--- diff --git a/docs/BOUNDARIES.md b/docs/BOUNDARIES.md new file mode 100644 index 000000000..d317a024f --- /dev/null +++ b/docs/BOUNDARIES.md @@ -0,0 +1,94 @@ +# Boundaries + +> Doc status: canonical. +> This file defines the current normative dependency and ownership rules for Macroflows. + +## Scope + +These are the default rules for new work and opportunistic cleanup. Existing legacy exceptions may remain during migration, but they should not become the pattern for new code. + +## Top-Level Ownership + +### `src/modules/*` + +Modules own business-facing semantics and use-case flows for their area. + +- Prefer module `application` as the cross-module integration surface. +- Do not import another module's `infrastructure` as an integration shortcut. +- Keep module-specific semantics inside the owning module instead of moving them into `src/shared/*`. + +### `src/sections/*` + +Sections own page and feature composition. + +- Compose module application flows and UI pieces. +- Do not become the canonical home of domain truth. +- Do not define cross-module semantic rules that belong in a module. + +### `src/shared/*` + +Shared owns technical cross-cutting helpers and framework support. + +- May serve modules and sections. +- Must not become a dumping ground for feature-specific business semantics. +- Must not depend on `src/sections/*`. + +### `src/routes/*` + +Routes own route entrypoints and API endpoints. + +- Compose sections and application flows. +- Must not become a second business-logic layer. + +### `src/di/*` + +DI owns dependency wiring and default composition. + +- May import across modules for assembly. +- Must not define or reinterpret business rules. + +## Layer Rules + +### Domain + +- Owns business rules, validation shapes, and contracts. +- Must not import UI, routes, Supabase clients, toasts, or telemetry side effects. +- Uses standard `Error` with descriptive messages and optional `cause`. +- Do not introduce custom error hierarchies by default. + +### Application + +- Orchestrates use cases, error handling, and side effects. +- May depend on domain and infrastructure. +- Handles user-facing feedback with `showError` and telemetry with `logging`. +- Should be the default integration layer used by sections and routes. + +### Infrastructure + +- Owns external systems, repositories, gateways, caches, and transport details. +- Implements contracts owned by modules. +- Must not become the place where canonical business semantics are invented. + +### UI + +- Owns rendering and interaction concerns. +- May compose application flows and view-model shaping for presentation. +- Must not redefine canonical business truth that belongs in a module. + +## Cross-Module Rules + +- Prefer composition through module application APIs instead of direct domain or infrastructure coupling. +- Avoid creating backdoor dependencies from one feature into another feature's internal storage, transport, or cache implementation. +- If a shared rule is actually business-specific, move it to the owning module instead of `src/shared/*`. +- If a decision changes ownership boundaries, update this file and the relevant ADR in the same change. + +## Transitional Reality + +`src/modules/diet/*` is still a large transitional macro-context. Canonical docs should describe its current ownership honestly and avoid pretending the repo already has a fully separated bounded-context model. + +## See Also + +- `../AGENTS.md` +- `./ARCHITECTURE.md` +- `./DOCS_GOVERNANCE.md` +- `./adr/0002-current-repo-architecture-and-boundaries.md` diff --git a/docs/CODESTYLE_GUIDE.md b/docs/CODESTYLE_GUIDE.md index 5f7990770..c2daa462b 100644 --- a/docs/CODESTYLE_GUIDE.md +++ b/docs/CODESTYLE_GUIDE.md @@ -1,5 +1,8 @@ # Macroflows – Concrete Codebase Style & Anti-Patterns Guide +> Doc status: supporting. +> This document preserves useful examples and historical guidance. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-07-08_ This document provides **concrete, specific guidelines** for the Macroflows codebase, based on actual patterns found in the code and specific improvements needed. @@ -329,4 +332,4 @@ export function canEditGroup(user: User, group: ItemGroup, screenState: ScreenSt - [ ] Usage is limited to event handlers, parallel effects, or non-critical callbacks. - [ ] The reason for `void` is documented if not obvious. ---- \ No newline at end of file +--- diff --git a/docs/COPILOT_SHORT_GUIDE.md b/docs/COPILOT_SHORT_GUIDE.md index cbc617a4e..edcfd4108 100644 --- a/docs/COPILOT_SHORT_GUIDE.md +++ b/docs/COPILOT_SHORT_GUIDE.md @@ -1,6 +1,11 @@ # Copilot Short Guide -See `.github/copilot-instructions.md` for the full instructions. +> Doc status: supporting. +> This document preserves Copilot-specific guidance. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + +Use [`../AGENTS.md`](../AGENTS.md) as the canonical repo-wide policy. + +Keep [`.github/copilot-instructions.md`](../.github/copilot-instructions.md) as the Copilot transport entrypoint. - Use descriptive, action-based names. - Never use side-effect utilities (like `showError`) in domain code. @@ -14,4 +19,4 @@ See `.github/copilot-instructions.md` for the full instructions. - Never use dynamic imports. Always use static imports at the top. ## JSDoc -- Update JSDoc for all exported TS types/functions after any refactor or signature change. \ No newline at end of file +- Update JSDoc for all exported TS types/functions after any refactor or signature change. diff --git a/docs/DEPRECATION_PLAN_V0.14.0.md b/docs/DEPRECATION_PLAN_V0.14.0.md index 7c1dc0dd9..1f6eb6da6 100644 --- a/docs/DEPRECATION_PLAN_V0.14.0.md +++ b/docs/DEPRECATION_PLAN_V0.14.0.md @@ -1,5 +1,8 @@ # Legacy Entity Migration and Removal Plan (Item/ItemGroup) - v0.14.0 +> Doc status: supporting. +> This document preserves migration history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Created: June 18, 2025_ _Status: Implementation ready_ _Author: AI Assistant based on Phases 1, 2, and 3 implementation_ diff --git a/docs/DOCS_GOVERNANCE.md b/docs/DOCS_GOVERNANCE.md new file mode 100644 index 000000000..815c5a955 --- /dev/null +++ b/docs/DOCS_GOVERNANCE.md @@ -0,0 +1,84 @@ +# Docs Governance + +> Doc status: canonical. +> This file defines documentation lifecycle, ownership, precedence, and update workflow for Macroflows. + +## Purpose + +Macroflows keeps historical material, audits, and tool-specific assets on purpose. This file prevents those materials from competing with the current source of truth. + +## Canonical Docs + +The current canonical set is: + +- `../AGENTS.md` +- `./ARCHITECTURE.md` +- `./BOUNDARIES.md` +- `./DOCS_GOVERNANCE.md` +- `./adr/README.md` +- accepted ADRs under `./adr/` + +Canonical docs must stay aligned. If a change updates one canonical doc and affects the others, update the related canonical docs in the same PR or commit. + +## Ownership + +- Owner: the repository maintainer. +- Canonical doc changes are part of implementation work, not optional cleanup. +- Any change to `AGENTS.md`, `docs/ARCHITECTURE.md`, `docs/BOUNDARIES.md`, `docs/DOCS_GOVERNANCE.md`, or `docs/adr/*` must leave the canonical set internally consistent. + +## Precedence + +1. The most specific canonical doc for the subject +2. `AGENTS.md` as the canonical repo-wide entrypoint +3. Pointer files such as `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md` +4. Supporting docs +5. Deprecated docs +6. Archived docs + +ADRs record decision history. They do not silently override current canon. If an ADR changes current policy, the canonical docs must be updated at the same time. + +## Lifecycle Statuses + +Use one of these statuses near the top of each managed doc: + +- `canonical`: current source of truth for an active concern +- `pointer`: thin entrypoint that forwards readers to canonical docs +- `supporting`: useful context, examples, audits, migration notes, or tool-specific guidance that is not authoritative +- `deprecated`: still present but scheduled for removal or replacement +- `archived`: historical material retained for reference only + +## Banner Rules + +- Canonical docs declare `Doc status: canonical.` +- Pointer docs declare `Doc status: pointer.` +- Supporting docs declare `Doc status: supporting.` and say they are not a source of truth. +- Archived docs declare `Doc status: archived.` and live under `docs/archive/` when practical. + +## Update Workflow + +Use the smallest document that matches the change: + +- Small repo-wide agent rule or workflow default: update `AGENTS.md` +- Dependency direction or ownership change: update `docs/BOUNDARIES.md` +- Current structure or system map change: update `docs/ARCHITECTURE.md` +- Cross-cutting or long-lived decision: add or supersede an ADR and update canon in the same change +- Audits, investigations, migration plans, and implementation notes: supporting or archived docs only + +## Enforcement + +`pnpm docs:check` validates the docs-governance contract. It is part of `pnpm check` and therefore part of CI. + +The check verifies: + +- required canonical files exist +- root pointer files point to `AGENTS.md` and state precedence +- canonical docs do not prefer legacy quality-gate commands +- top-level docs do not describe `.github/copilot-instructions.md` as the main instruction file +- supporting and archived docs that are part of the managed set contain a status banner +- ADR numbering and index/template references are consistent + +## See Also + +- `../AGENTS.md` +- `./README.md` +- `./adr/README.md` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..0a25f397f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,54 @@ +# Documentation Index + +> Doc status: canonical. +> This file maps Macroflows documentation by status so contributors and agents can find the current source of truth quickly. + +## Read First + +For repo-wide changes, read these first: + +1. `../AGENTS.md` +2. `./BOUNDARIES.md` +3. `./ARCHITECTURE.md` +4. `./DOCS_GOVERNANCE.md` +5. Relevant ADRs under `./adr/` + +## Canonical + +- `../AGENTS.md` — repo-wide agent policy, precedence summary, and workflow routing +- `./BOUNDARIES.md` — normative ownership and dependency rules +- `./ARCHITECTURE.md` — current architecture map and transitional realities +- `./DOCS_GOVERNANCE.md` — lifecycle, ownership, precedence, and banner rules +- `./adr/README.md` — ADR index and usage rules + +## Pointer + +- `../CLAUDE.md` +- `../GEMINI.md` +- `../.github/copilot-instructions.md` + +Pointer docs exist for tool-specific entrypoints only. They are not canonical. + +## Supporting + +- `./ARCHITECTURE_GUIDE.md` +- `./CODESTYLE_GUIDE.md` +- `./ARCHITECTURE_AUDIT.md` +- `./audit_domain.md` +- `./audit_domain_diet.md` +- `./audit_domain_diet_food.md` +- `./audit_domain_diet_recipe.md` +- `./audit_sections.md` +- `./COPILOT_SHORT_GUIDE.md` +- `../DI-migration-plan.md` +- `./RECIPE_MIGRATION_AUDIT.md` +- `./DEPRECATION_PLAN_V0.14.0.md` +- `../.github/COPILOT_SETUP_VALIDATION.md` + +Supporting docs preserve context, examples, audits, or migration notes. They are not a source of truth. + +## Archived + +- `./archive/TODO-REPO-DOC.md` + +Archived docs are historical material retained for reference only. diff --git a/docs/RECIPE_MIGRATION_AUDIT.md b/docs/RECIPE_MIGRATION_AUDIT.md index 8f48cfece..26ab5f8f7 100644 --- a/docs/RECIPE_MIGRATION_AUDIT.md +++ b/docs/RECIPE_MIGRATION_AUDIT.md @@ -1,5 +1,8 @@ reportedBy: recipe-migration-agent.v1 +> Doc status: supporting. +> This document preserves migration history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + # Recipe Entity Migration Audit: Legacy Item[] → UnifiedItem[] This document provides a comprehensive audit of all Recipe entity usages that need to be migrated from using legacy Item[] to UnifiedItem[] in-memory, while maintaining Item[] compatibility for database persistence. diff --git a/docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md b/docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md new file mode 100644 index 000000000..2dd50fc1a --- /dev/null +++ b/docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md @@ -0,0 +1,34 @@ +# ADR 0001: Canonical agent instructions and doc precedence + +> Status: accepted +> Date: 2026-03-24 + +## Context + +Macroflows accumulated multiple assistant-specific instruction files over time, including `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md`. They grew independently, started to overlap, and eventually contradicted one another. + +The repo also accumulated audits, migration plans, and tool-specific prompt assets. Those documents contain useful context, but they should not compete with the current source of truth. + +## Decision + +Macroflows adopts a canonical documentation model: + +- `AGENTS.md` is the canonical repo-wide agent entrypoint. +- `docs/BOUNDARIES.md`, `docs/ARCHITECTURE.md`, and `docs/DOCS_GOVERNANCE.md` are canonical within their specific scopes. +- `docs/adr/` stores decision history, not silent policy overrides. +- `CLAUDE.md`, `GEMINI.md`, and `.github/copilot-instructions.md` become thin pointer files. +- Supporting, deprecated, and archived docs must be clearly labeled so they cannot be mistaken for canon. + +## Consequences + +- The repo gets a single, explicit read-first path for humans and agents. +- Tool-specific entrypoints remain compatible with existing ecosystems without owning policy. +- Historical docs remain available, but source-of-truth ambiguity is removed. +- Canonical docs must be updated together when policy changes cross document boundaries. + +## Canon Sync + +- `../../AGENTS.md` +- `../BOUNDARIES.md` +- `../ARCHITECTURE.md` +- `../DOCS_GOVERNANCE.md` diff --git a/docs/adr/0002-current-repo-architecture-and-boundaries.md b/docs/adr/0002-current-repo-architecture-and-boundaries.md new file mode 100644 index 000000000..8911553ae --- /dev/null +++ b/docs/adr/0002-current-repo-architecture-and-boundaries.md @@ -0,0 +1,35 @@ +# ADR 0002: Current repo architecture and boundaries + +> Status: accepted +> Date: 2026-03-24 + +## Context + +Macroflows already has strong module and layering patterns, but the real repo structure is more nuanced than a simplified clean-architecture diagram suggests. In particular, `src/modules/diet/*` behaves as a large transitional macro-context with several internal subdomains. + +Without an explicit architectural record, future docs risk describing an aspirational architecture that the codebase does not yet match. + +## Decision + +Macroflows documents the current architecture honestly: + +- `src/modules/*` owns business-facing module logic +- `src/sections/*` owns feature and page composition +- `src/shared/*` owns technical cross-cutting code +- `src/routes/*` owns route entrypoints and API endpoints +- `src/di/*` owns dependency wiring and composition +- `src/modules/diet/*` is treated as a large transitional macro-context, not a completed bounded-context split + +Dependency and ownership rules are defined normatively in `docs/BOUNDARIES.md`, while the structure itself is described in `docs/ARCHITECTURE.md`. + +## Consequences + +- Canonical docs no longer pretend the repo is more normalized than it is. +- Boundary discussions can be grounded in current code instead of abstract diagrams. +- Future structural refactors can update canon incrementally as the code changes. + +## Canon Sync + +- `../../AGENTS.md` +- `../BOUNDARIES.md` +- `../ARCHITECTURE.md` diff --git a/docs/adr/0003-error-handling-and-simplification-defaults.md b/docs/adr/0003-error-handling-and-simplification-defaults.md new file mode 100644 index 000000000..008c399f3 --- /dev/null +++ b/docs/adr/0003-error-handling-and-simplification-defaults.md @@ -0,0 +1,38 @@ +# ADR 0003: Error handling and simplification defaults + +> Status: accepted +> Date: 2026-03-24 + +## Context + +Existing repo instructions diverged on several high-impact defaults: + +- whether domain code should use custom error hierarchies +- which error-reporting utilities should be treated as canonical +- whether new work should default to compatibility scaffolding, rollback logic, and fallback-heavy patterns +- which repository quality gate should be treated as the default + +Those differences create inconsistent guidance for both humans and agents. + +## Decision + +Macroflows standardizes these defaults: + +- Prefer `pnpm` and `pnpm check` for repository workflows. +- Domain code uses standard `Error` with descriptive messages and optional `cause`. +- Do not introduce custom error hierarchies by default. +- Application and UI layers handle user-facing feedback with `showError` and telemetry with `logging`. +- Do not add new feature-flag, rollout, rollback, or fallback scaffolding by default. +- Preserve compatibility code that still supports active model or data transitions already present in the repo. + +## Consequences + +- Repo-wide defaults become easier to apply consistently. +- New instructions stop inheriting older model-specific workarounds and contradictory patterns. +- Simplification remains the default without erasing legitimate compatibility work already in progress. + +## Canon Sync + +- `../../AGENTS.md` +- `../BOUNDARIES.md` +- `../DOCS_GOVERNANCE.md` diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 000000000..937bd2d07 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,22 @@ +# Architecture Decision Records + +> Doc status: canonical. +> This directory contains accepted ADRs and the ADR template for Macroflows. + +## Rules + +- Use zero-padded numbering: `0001-...`, `0002-...`, and so on. +- Create a new ADR for cross-cutting or long-lived decisions. +- If an ADR changes current policy, update the canonical docs in the same PR or commit. +- Do not rely on ADRs alone to express current repo policy; keep `AGENTS.md`, `docs/BOUNDARIES.md`, `docs/ARCHITECTURE.md`, and `docs/DOCS_GOVERNANCE.md` in sync. +- When an ADR is replaced, mark it as superseded and link both records. + +## Template + +- `./_template.md` + +## Index + +- `0001-canonical-agent-instructions-and-doc-precedence.md` +- `0002-current-repo-architecture-and-boundaries.md` +- `0003-error-handling-and-simplification-defaults.md` diff --git a/docs/adr/_template.md b/docs/adr/_template.md new file mode 100644 index 000000000..9eb012fb2 --- /dev/null +++ b/docs/adr/_template.md @@ -0,0 +1,20 @@ +# ADR XXXX: Title + +> Status: proposed +> Date: YYYY-MM-DD + +## Context + +Describe the problem, competing forces, and why a decision is needed. + +## Decision + +State the decision in direct, current-tense language. + +## Consequences + +List the important outcomes, tradeoffs, follow-up work, and migration notes. + +## Canon Sync + +List the canonical docs that must be updated when this ADR is accepted or superseded. diff --git a/docs/TODO-REPO-DOC.md b/docs/archive/TODO-REPO-DOC.md similarity index 98% rename from docs/TODO-REPO-DOC.md rename to docs/archive/TODO-REPO-DOC.md index 218948906..6df66f689 100644 --- a/docs/TODO-REPO-DOC.md +++ b/docs/archive/TODO-REPO-DOC.md @@ -1,5 +1,8 @@ # TODO: Repository Documentation Enhancement Plan +> Doc status: archived. +> This document is retained for historical reference only. Current canon lives in `../../AGENTS.md` and the canonical docs under `../`. + ## 📋 **PROFESSIONAL TECHNICAL DOCUMENTATION PLAN** ### **🎯 Objective: Contextualize architectural decisions as a senior developer would** diff --git a/docs/audit_domain.md b/docs/audit_domain.md index ca66d3e0a..3c28f24b4 100644 --- a/docs/audit_domain.md +++ b/docs/audit_domain.md @@ -1,5 +1,8 @@ # Domain Layer Audit – High-Level Review +> Doc status: supporting. +> This document preserves audit history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-07-08_ ## General Assessment diff --git a/docs/audit_domain_diet.md b/docs/audit_domain_diet.md index 96ca07143..71c4db160 100644 --- a/docs/audit_domain_diet.md +++ b/docs/audit_domain_diet.md @@ -1,5 +1,8 @@ # Diet Domain Audit +> Doc status: supporting. +> This document preserves audit history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-06-07_ ## Overview diff --git a/docs/audit_domain_diet_food.md b/docs/audit_domain_diet_food.md index d74172379..0d729616b 100644 --- a/docs/audit_domain_diet_food.md +++ b/docs/audit_domain_diet_food.md @@ -1,5 +1,8 @@ # Diet Domain Audit – Food Submodule +> Doc status: supporting. +> This document preserves audit history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-07-08_ ## Overview diff --git a/docs/audit_domain_diet_recipe.md b/docs/audit_domain_diet_recipe.md index af38139a1..97f8b9bd9 100644 --- a/docs/audit_domain_diet_recipe.md +++ b/docs/audit_domain_diet_recipe.md @@ -1,5 +1,8 @@ # Diet Domain Audit – Recipe Submodule +> Doc status: supporting. +> This document preserves audit history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-06-07_ ## Overview diff --git a/docs/audit_sections.md b/docs/audit_sections.md index 269efa6dd..7202c018c 100644 --- a/docs/audit_sections.md +++ b/docs/audit_sections.md @@ -1,5 +1,8 @@ # Sections/UI Layer Audit +> Doc status: supporting. +> This document preserves audit history and implementation context. It is not a source of truth; use `../AGENTS.md` and the canonical docs instead. + _Last updated: 2025-07-08_ ## Overview diff --git a/package.json b/package.json index 6cdf8fa5d..a6f587d16 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,10 @@ "check-unused-exports-strict": "ts-unused-exports .ts-unused-exports.json", "check-unused-exports": "ts-unused-exports .ts-unused-exports.json || echo 'Warning: Found unused exports. Consider removing them to improve code quality.'", "check:no-emoji": "node scripts/check-no-emoji.mjs", - "check": "run-p flint type-check test", + "check": "run-p docs:check flint type-check test", "copilot:check": "pnpm run check 2>&1 && echo 'COPILOT: All checks passed!' || echo 'COPILOT: Some checks failed!'", "dev": "pnpm run gen-app-version; vinxi dev", + "docs:check": "node scripts/check-doc-governance.mjs", "fix": "eslint . --fix --cache >/dev/null 2>&1 || exit 0", "flint": "pnpm run fix && pnpm run lint", "gen-app-version": "bash ./.scripts/gen-app-version.sh", diff --git a/scripts/check-doc-governance.mjs b/scripts/check-doc-governance.mjs new file mode 100644 index 000000000..8d9eeffa0 --- /dev/null +++ b/scripts/check-doc-governance.mjs @@ -0,0 +1,180 @@ +import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs' +import path from 'node:path' + +const repoRoot = process.cwd() + +const requiredCanonicalFiles = [ + 'AGENTS.md', + 'docs/ARCHITECTURE.md', + 'docs/BOUNDARIES.md', + 'docs/DOCS_GOVERNANCE.md', + 'docs/README.md', + 'docs/adr/README.md', + 'docs/adr/_template.md', + 'docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md', + 'docs/adr/0002-current-repo-architecture-and-boundaries.md', + 'docs/adr/0003-error-handling-and-simplification-defaults.md', +] + +const pointerFiles = [ + 'CLAUDE.md', + 'GEMINI.md', + '.github/copilot-instructions.md', +] + +const supportingDocs = [ + '.github/README.md', + '.github/copilot-commit-message-instructions.md', + 'docs/ARCHITECTURE_GUIDE.md', + 'docs/CODESTYLE_GUIDE.md', + 'docs/ARCHITECTURE_AUDIT.md', + 'docs/audit_domain.md', + 'docs/audit_domain_diet.md', + 'docs/audit_domain_diet_food.md', + 'docs/audit_domain_diet_recipe.md', + 'docs/audit_sections.md', + 'docs/COPILOT_SHORT_GUIDE.md', + 'docs/RECIPE_MIGRATION_AUDIT.md', + 'docs/DEPRECATION_PLAN_V0.14.0.md', + 'DI-migration-plan.md', + '.github/COPILOT_SETUP_VALIDATION.md', +] + +const archivedDocs = ['docs/archive/TODO-REPO-DOC.md'] + +const managedPromptFiles = [ + '.github/prompts/issues-worktree.prompt.md', + '.github/prompts/refine-github-issue.prompt.md', + '.github/prompts/refactor.prompt.md', + '.github/prompts/pr-reviews.prompt.md', + '.github/prompts/code-review.prompt.md', +] + +const canonicalDocsForLegacyCommandScan = [ + 'AGENTS.md', + 'docs/ARCHITECTURE.md', + 'docs/BOUNDARIES.md', + 'docs/DOCS_GOVERNANCE.md', + 'docs/README.md', +] + +const topLevelDocsForCopilotMainFileScan = [ + 'README.md', + 'docs/COPILOT_SHORT_GUIDE.md', + '.github/COPILOT_SETUP_VALIDATION.md', +] + +const failures = [] + +function read(relativePath) { + return readFileSync(path.join(repoRoot, relativePath), 'utf8') +} + +function assert(condition, message) { + if (!condition) { + failures.push(message) + } +} + +function assertBanner(relativePath, expectedStatus) { + const content = read(relativePath) + const head = content.split('\n').slice(0, 12).join('\n') + assert( + head.includes(`Doc status: ${expectedStatus}.`), + `${relativePath} must declare "Doc status: ${expectedStatus}." near the top`, + ) +} + +for (const relativePath of requiredCanonicalFiles) { + assert(existsSync(path.join(repoRoot, relativePath)), `Missing required canonical file: ${relativePath}`) +} + +for (const relativePath of ['AGENTS.md', 'docs/ARCHITECTURE.md', 'docs/BOUNDARIES.md', 'docs/DOCS_GOVERNANCE.md', 'docs/README.md', 'docs/adr/README.md']) { + if (existsSync(path.join(repoRoot, relativePath))) { + assertBanner(relativePath, 'canonical') + } +} + +for (const relativePath of pointerFiles) { + const content = read(relativePath) + assert(content.includes('Doc status: pointer.'), `${relativePath} must declare pointer status`) + assert(content.includes('AGENTS.md'), `${relativePath} must point to AGENTS.md`) + assert( + /canonical docs win/i.test(content), + `${relativePath} must state that canonical docs win on conflicts`, + ) +} + +for (const relativePath of supportingDocs) { + assert(existsSync(path.join(repoRoot, relativePath)), `Missing managed supporting doc: ${relativePath}`) + if (existsSync(path.join(repoRoot, relativePath))) { + assertBanner(relativePath, 'supporting') + } +} + +for (const relativePath of archivedDocs) { + assert(existsSync(path.join(repoRoot, relativePath)), `Missing managed archived doc: ${relativePath}`) + if (existsSync(path.join(repoRoot, relativePath))) { + assertBanner(relativePath, 'archived') + } +} + +for (const relativePath of canonicalDocsForLegacyCommandScan) { + const content = read(relativePath) + assert(!content.includes('npm run check'), `${relativePath} must not prefer "npm run check"`) + assert(!content.includes('npm run copilot:check'), `${relativePath} must not prefer the legacy Copilot quality gate`) +} + +for (const relativePath of topLevelDocsForCopilotMainFileScan) { + const content = read(relativePath) + assert( + !/main instruction file/i.test(content), + `${relativePath} must not describe .github/copilot-instructions.md as the main instruction file`, + ) + assert( + !/See `?\.github\/copilot-instructions\.md`? for the full instructions\./i.test(content), + `${relativePath} must not send readers to .github/copilot-instructions.md as the full instruction source`, + ) +} + +for (const relativePath of managedPromptFiles) { + const content = read(relativePath) + assert(content.includes('AGENTS.md'), `${relativePath} must reference AGENTS.md for repo-wide rules`) + assert( + !content.includes('copilot-instructions.md](../copilot-instructions.md)'), + `${relativePath} must not route repo-wide rules through .github/copilot-instructions.md`, + ) +} + +const adrDir = path.join(repoRoot, 'docs/adr') +const adrFiles = readdirSync(adrDir) + .filter((fileName) => /^\d{4}-.*\.md$/.test(fileName)) + .sort() + +assert(adrFiles.length >= 3, 'docs/adr must contain the bootstrap ADR files') +for (const fileName of adrFiles) { + assert(/^\d{4}-/.test(fileName), `ADR file must use zero-padded numbering: ${fileName}`) +} + +const adrReadme = read('docs/adr/README.md') +for (const fileName of adrFiles) { + assert(adrReadme.includes(fileName), `docs/adr/README.md must reference ${fileName}`) +} +assert(adrReadme.includes('./_template.md'), 'docs/adr/README.md must reference ./_template.md') + +for (const relativePath of requiredCanonicalFiles) { + assert( + statSync(path.join(repoRoot, relativePath)).isFile(), + `${relativePath} must be a file`, + ) +} + +if (failures.length > 0) { + console.error('docs:check failed:') + for (const failure of failures) { + console.error(`- ${failure}`) + } + process.exit(1) +} + +console.log('docs:check passed') From 9abc7b5e80cd1eaa7aee10a5ba1ee45dc777373a Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Tue, 24 Mar 2026 20:57:59 -0300 Subject: [PATCH 3/4] docs: update README with project documentation structure --- docs/locale/pt-br/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/locale/pt-br/README.md b/docs/locale/pt-br/README.md index 99c09cc49..dea3361e8 100644 --- a/docs/locale/pt-br/README.md +++ b/docs/locale/pt-br/README.md @@ -66,6 +66,14 @@ Macroflows é um sistema de rastreamento nutricional focado em tipagem forte, in Por enquanto, está focado em ser um projeto pessoal para acompanhar minha própria nutrição, mas talvez no futuro se torne um produto SaaS. +## Documentação do projeto + +- Ponto de entrada padrão para agentes em todo o repositório: [AGENTS.md](./AGENTS.md) +- Mapa da arquitetura padrão: [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) +- Regras canônicas de dependência e propriedade: [docs/BOUNDARIES.md](./docs/BOUNDARIES.md) +- Governança da documentação canônica: [docs/DOCS_GOVERNANCE.md](./docs/DOCS_GOVERNANCE.md) +- Índice ADR: [docs/adr/README.md](./docs/adr/README.md) + --- ## Funcionalidades From ad1d79f0e2b155795fd17b1ce6526efa73d1d994 Mon Sep 17 00:00:00 2001 From: marcuscastelo Date: Tue, 24 Mar 2026 21:12:36 -0300 Subject: [PATCH 4/4] chore(pr): apply PR #1476 suggestions --- scripts/check-doc-governance.mjs | 299 +++++++++++++++++--------- scripts/check-doc-governance.test.mjs | 186 ++++++++++++++++ 2 files changed, 387 insertions(+), 98 deletions(-) create mode 100644 scripts/check-doc-governance.test.mjs diff --git a/scripts/check-doc-governance.mjs b/scripts/check-doc-governance.mjs index 8d9eeffa0..dc0267479 100644 --- a/scripts/check-doc-governance.mjs +++ b/scripts/check-doc-governance.mjs @@ -1,9 +1,10 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs' import path from 'node:path' +import { fileURLToPath } from 'node:url' -const repoRoot = process.cwd() +const thisFilePath = fileURLToPath(import.meta.url) -const requiredCanonicalFiles = [ +const REQUIRED_CANONICAL_FILES = [ 'AGENTS.md', 'docs/ARCHITECTURE.md', 'docs/BOUNDARIES.md', @@ -16,13 +17,13 @@ const requiredCanonicalFiles = [ 'docs/adr/0003-error-handling-and-simplification-defaults.md', ] -const pointerFiles = [ +const POINTER_FILES = [ 'CLAUDE.md', 'GEMINI.md', '.github/copilot-instructions.md', ] -const supportingDocs = [ +const SUPPORTING_DOCS = [ '.github/README.md', '.github/copilot-commit-message-instructions.md', 'docs/ARCHITECTURE_GUIDE.md', @@ -40,9 +41,9 @@ const supportingDocs = [ '.github/COPILOT_SETUP_VALIDATION.md', ] -const archivedDocs = ['docs/archive/TODO-REPO-DOC.md'] +const ARCHIVED_DOCS = ['docs/archive/TODO-REPO-DOC.md'] -const managedPromptFiles = [ +const MANAGED_PROMPT_FILES = [ '.github/prompts/issues-worktree.prompt.md', '.github/prompts/refine-github-issue.prompt.md', '.github/prompts/refactor.prompt.md', @@ -50,7 +51,7 @@ const managedPromptFiles = [ '.github/prompts/code-review.prompt.md', ] -const canonicalDocsForLegacyCommandScan = [ +const CANONICAL_DOCS_FOR_LEGACY_COMMAND_SCAN = [ 'AGENTS.md', 'docs/ARCHITECTURE.md', 'docs/BOUNDARIES.md', @@ -58,123 +59,225 @@ const canonicalDocsForLegacyCommandScan = [ 'docs/README.md', ] -const topLevelDocsForCopilotMainFileScan = [ +const TOP_LEVEL_DOCS_FOR_COPILOT_MAIN_FILE_SCAN = [ 'README.md', 'docs/COPILOT_SHORT_GUIDE.md', '.github/COPILOT_SETUP_VALIDATION.md', ] -const failures = [] +export function checkDocsGovernance(repoRoot = process.cwd()) { + const failures = [] -function read(relativePath) { - return readFileSync(path.join(repoRoot, relativePath), 'utf8') -} + function absolutePath(relativePath) { + return path.join(repoRoot, relativePath) + } -function assert(condition, message) { - if (!condition) { - failures.push(message) + function pathExists(relativePath) { + return existsSync(absolutePath(relativePath)) } -} -function assertBanner(relativePath, expectedStatus) { - const content = read(relativePath) - const head = content.split('\n').slice(0, 12).join('\n') - assert( - head.includes(`Doc status: ${expectedStatus}.`), - `${relativePath} must declare "Doc status: ${expectedStatus}." near the top`, - ) -} + function isFile(relativePath) { + try { + return statSync(absolutePath(relativePath)).isFile() + } catch { + return false + } + } -for (const relativePath of requiredCanonicalFiles) { - assert(existsSync(path.join(repoRoot, relativePath)), `Missing required canonical file: ${relativePath}`) -} + function isDirectory(relativePath) { + try { + return statSync(absolutePath(relativePath)).isDirectory() + } catch { + return false + } + } -for (const relativePath of ['AGENTS.md', 'docs/ARCHITECTURE.md', 'docs/BOUNDARIES.md', 'docs/DOCS_GOVERNANCE.md', 'docs/README.md', 'docs/adr/README.md']) { - if (existsSync(path.join(repoRoot, relativePath))) { - assertBanner(relativePath, 'canonical') + function read(relativePath) { + try { + return readFileSync(absolutePath(relativePath), 'utf8') + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + failures.push(`Failed to read ${relativePath}: ${message}`) + return null + } } -} -for (const relativePath of pointerFiles) { - const content = read(relativePath) - assert(content.includes('Doc status: pointer.'), `${relativePath} must declare pointer status`) - assert(content.includes('AGENTS.md'), `${relativePath} must point to AGENTS.md`) - assert( - /canonical docs win/i.test(content), - `${relativePath} must state that canonical docs win on conflicts`, - ) -} + function assert(condition, message) { + if (!condition) { + failures.push(message) + } + } + + function assertBanner(relativePath, expectedStatus) { + const content = read(relativePath) + if (content === null) { + return + } -for (const relativePath of supportingDocs) { - assert(existsSync(path.join(repoRoot, relativePath)), `Missing managed supporting doc: ${relativePath}`) - if (existsSync(path.join(repoRoot, relativePath))) { - assertBanner(relativePath, 'supporting') + const head = content.split('\n').slice(0, 12).join('\n') + assert( + head.includes(`Doc status: ${expectedStatus}.`), + `${relativePath} must declare "Doc status: ${expectedStatus}." near the top`, + ) } -} -for (const relativePath of archivedDocs) { - assert(existsSync(path.join(repoRoot, relativePath)), `Missing managed archived doc: ${relativePath}`) - if (existsSync(path.join(repoRoot, relativePath))) { - assertBanner(relativePath, 'archived') + for (const relativePath of REQUIRED_CANONICAL_FILES) { + assert(pathExists(relativePath), `Missing required canonical file: ${relativePath}`) } -} -for (const relativePath of canonicalDocsForLegacyCommandScan) { - const content = read(relativePath) - assert(!content.includes('npm run check'), `${relativePath} must not prefer "npm run check"`) - assert(!content.includes('npm run copilot:check'), `${relativePath} must not prefer the legacy Copilot quality gate`) -} + for (const relativePath of [ + 'AGENTS.md', + 'docs/ARCHITECTURE.md', + 'docs/BOUNDARIES.md', + 'docs/DOCS_GOVERNANCE.md', + 'docs/README.md', + 'docs/adr/README.md', + ]) { + if (pathExists(relativePath)) { + assertBanner(relativePath, 'canonical') + } + } -for (const relativePath of topLevelDocsForCopilotMainFileScan) { - const content = read(relativePath) - assert( - !/main instruction file/i.test(content), - `${relativePath} must not describe .github/copilot-instructions.md as the main instruction file`, - ) - assert( - !/See `?\.github\/copilot-instructions\.md`? for the full instructions\./i.test(content), - `${relativePath} must not send readers to .github/copilot-instructions.md as the full instruction source`, - ) -} + for (const relativePath of POINTER_FILES) { + assert(pathExists(relativePath), `Missing pointer file: ${relativePath}`) + if (!isFile(relativePath)) { + assert(false, `${relativePath} must be a file`) + continue + } -for (const relativePath of managedPromptFiles) { - const content = read(relativePath) - assert(content.includes('AGENTS.md'), `${relativePath} must reference AGENTS.md for repo-wide rules`) - assert( - !content.includes('copilot-instructions.md](../copilot-instructions.md)'), - `${relativePath} must not route repo-wide rules through .github/copilot-instructions.md`, - ) -} + const content = read(relativePath) + if (content === null) { + continue + } -const adrDir = path.join(repoRoot, 'docs/adr') -const adrFiles = readdirSync(adrDir) - .filter((fileName) => /^\d{4}-.*\.md$/.test(fileName)) - .sort() + assert(content.includes('Doc status: pointer.'), `${relativePath} must declare pointer status`) + assert(content.includes('AGENTS.md'), `${relativePath} must point to AGENTS.md`) + assert( + /canonical docs win/i.test(content), + `${relativePath} must state that canonical docs win on conflicts`, + ) + } -assert(adrFiles.length >= 3, 'docs/adr must contain the bootstrap ADR files') -for (const fileName of adrFiles) { - assert(/^\d{4}-/.test(fileName), `ADR file must use zero-padded numbering: ${fileName}`) -} + for (const relativePath of SUPPORTING_DOCS) { + assert(pathExists(relativePath), `Missing managed supporting doc: ${relativePath}`) + if (pathExists(relativePath)) { + assertBanner(relativePath, 'supporting') + } + } -const adrReadme = read('docs/adr/README.md') -for (const fileName of adrFiles) { - assert(adrReadme.includes(fileName), `docs/adr/README.md must reference ${fileName}`) -} -assert(adrReadme.includes('./_template.md'), 'docs/adr/README.md must reference ./_template.md') + for (const relativePath of ARCHIVED_DOCS) { + assert(pathExists(relativePath), `Missing managed archived doc: ${relativePath}`) + if (pathExists(relativePath)) { + assertBanner(relativePath, 'archived') + } + } + + for (const relativePath of CANONICAL_DOCS_FOR_LEGACY_COMMAND_SCAN) { + if (!pathExists(relativePath) || !isFile(relativePath)) { + continue + } -for (const relativePath of requiredCanonicalFiles) { - assert( - statSync(path.join(repoRoot, relativePath)).isFile(), - `${relativePath} must be a file`, - ) + const content = read(relativePath) + if (content === null) { + continue + } + + assert(!content.includes('npm run check'), `${relativePath} must not prefer "npm run check"`) + assert(!content.includes('npm run copilot:check'), `${relativePath} must not prefer the legacy Copilot quality gate`) + } + + for (const relativePath of TOP_LEVEL_DOCS_FOR_COPILOT_MAIN_FILE_SCAN) { + if (!pathExists(relativePath) || !isFile(relativePath)) { + continue + } + + const content = read(relativePath) + if (content === null) { + continue + } + + assert( + !/main instruction file/i.test(content), + `${relativePath} must not describe .github/copilot-instructions.md as the main instruction file`, + ) + assert( + !/See `?\.github\/copilot-instructions\.md`? for the full instructions\./i.test(content), + `${relativePath} must not send readers to .github/copilot-instructions.md as the full instruction source`, + ) + } + + for (const relativePath of MANAGED_PROMPT_FILES) { + if (!pathExists(relativePath) || !isFile(relativePath)) { + continue + } + + const content = read(relativePath) + if (content === null) { + continue + } + + assert(content.includes('AGENTS.md'), `${relativePath} must reference AGENTS.md for repo-wide rules`) + assert( + !content.includes('copilot-instructions.md](../copilot-instructions.md)'), + `${relativePath} must not route repo-wide rules through .github/copilot-instructions.md`, + ) + } + + const adrDir = 'docs/adr' + const adrFiles = [] + + if (!pathExists(adrDir)) { + failures.push('docs/adr must exist and be a directory') + } else if (!isDirectory(adrDir)) { + failures.push('docs/adr must be a directory') + } else { + adrFiles.push( + ...readdirSync(absolutePath(adrDir)) + .filter((fileName) => /^\d{4}-.*\.md$/.test(fileName)) + .sort(), + ) + } + + assert(adrFiles.length >= 3, 'docs/adr must contain the bootstrap ADR files') + for (const fileName of adrFiles) { + assert(/^\d{4}-/.test(fileName), `ADR file must use zero-padded numbering: ${fileName}`) + } + + const adrReadme = pathExists('docs/adr/README.md') && isFile('docs/adr/README.md') + ? read('docs/adr/README.md') + : null + if (adrReadme !== null) { + for (const fileName of adrFiles) { + assert(adrReadme.includes(fileName), `docs/adr/README.md must reference ${fileName}`) + } + assert(adrReadme.includes('./_template.md'), 'docs/adr/README.md must reference ./_template.md') + } + + for (const relativePath of REQUIRED_CANONICAL_FILES) { + if (!pathExists(relativePath)) { + continue + } + assert(isFile(relativePath), `${relativePath} must be a file`) + } + + return failures } -if (failures.length > 0) { - console.error('docs:check failed:') - for (const failure of failures) { - console.error(`- ${failure}`) +export function runDocsGovernanceCheck(repoRoot = process.cwd()) { + const failures = checkDocsGovernance(repoRoot) + + if (failures.length > 0) { + console.error('docs:check failed:') + for (const failure of failures) { + console.error(`- ${failure}`) + } + return 1 } - process.exit(1) + + console.log('docs:check passed') + return 0 } -console.log('docs:check passed') +if (process.argv[1] && path.resolve(process.argv[1]) === thisFilePath) { + process.exit(runDocsGovernanceCheck()) +} diff --git a/scripts/check-doc-governance.test.mjs b/scripts/check-doc-governance.test.mjs new file mode 100644 index 000000000..ceca304a9 --- /dev/null +++ b/scripts/check-doc-governance.test.mjs @@ -0,0 +1,186 @@ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' +import os from 'node:os' +import path from 'node:path' + +import { afterEach, describe, expect, it } from 'vitest' + +import { checkDocsGovernance } from './check-doc-governance.mjs' + +const REQUIRED_CANONICAL_FILES = [ + 'AGENTS.md', + 'docs/ARCHITECTURE.md', + 'docs/BOUNDARIES.md', + 'docs/DOCS_GOVERNANCE.md', + 'docs/README.md', + 'docs/adr/README.md', + 'docs/adr/_template.md', + 'docs/adr/0001-canonical-agent-instructions-and-doc-precedence.md', + 'docs/adr/0002-current-repo-architecture-and-boundaries.md', + 'docs/adr/0003-error-handling-and-simplification-defaults.md', +] + +const POINTER_FILES = [ + 'CLAUDE.md', + 'GEMINI.md', + '.github/copilot-instructions.md', +] + +const SUPPORTING_DOCS = [ + '.github/README.md', + '.github/copilot-commit-message-instructions.md', + 'docs/ARCHITECTURE_GUIDE.md', + 'docs/CODESTYLE_GUIDE.md', + 'docs/ARCHITECTURE_AUDIT.md', + 'docs/audit_domain.md', + 'docs/audit_domain_diet.md', + 'docs/audit_domain_diet_food.md', + 'docs/audit_domain_diet_recipe.md', + 'docs/audit_sections.md', + 'docs/COPILOT_SHORT_GUIDE.md', + 'docs/RECIPE_MIGRATION_AUDIT.md', + 'docs/DEPRECATION_PLAN_V0.14.0.md', + 'DI-migration-plan.md', + '.github/COPILOT_SETUP_VALIDATION.md', +] + +const ARCHIVED_DOCS = ['docs/archive/TODO-REPO-DOC.md'] + +const MANAGED_PROMPT_FILES = [ + '.github/prompts/issues-worktree.prompt.md', + '.github/prompts/refine-github-issue.prompt.md', + '.github/prompts/refactor.prompt.md', + '.github/prompts/pr-reviews.prompt.md', + '.github/prompts/code-review.prompt.md', +] + +const tempDirs = new Set() + +function createTempRepo() { + const tempDir = mkdtempSync(path.join(os.tmpdir(), 'macroflows-docs-check-')) + tempDirs.add(tempDir) + return tempDir +} + +function writeFile(repoRoot, relativePath, content) { + const absolutePath = path.join(repoRoot, relativePath) + mkdirSync(path.dirname(absolutePath), { recursive: true }) + writeFileSync(absolutePath, content) +} + +function buildValidFixture(repoRoot) { + for (const relativePath of REQUIRED_CANONICAL_FILES) { + if (relativePath === 'docs/adr/README.md') { + writeFile( + repoRoot, + relativePath, + [ + '# ADR README', + '', + '> Doc status: canonical.', + '- 0001-canonical-agent-instructions-and-doc-precedence.md', + '- 0002-current-repo-architecture-and-boundaries.md', + '- 0003-error-handling-and-simplification-defaults.md', + '- ./_template.md', + '', + ].join('\n'), + ) + continue + } + + writeFile( + repoRoot, + relativePath, + ['# Canonical', '', '> Doc status: canonical.', '', 'pnpm check', 'AGENTS.md', ''].join('\n'), + ) + } + + for (const relativePath of POINTER_FILES) { + writeFile( + repoRoot, + relativePath, + [ + '# Pointer', + '', + '> Doc status: pointer.', + '', + 'Canonical repo policy lives in AGENTS.md.', + 'If anything in this file conflicts with AGENTS.md or the canonical docs, the canonical docs win.', + '', + ].join('\n'), + ) + } + + for (const relativePath of SUPPORTING_DOCS) { + writeFile( + repoRoot, + relativePath, + ['# Supporting', '', '> Doc status: supporting.', '> This document is not a source of truth.', ''].join('\n'), + ) + } + + for (const relativePath of ARCHIVED_DOCS) { + writeFile( + repoRoot, + relativePath, + ['# Archived', '', '> Doc status: archived.', '> Historical only.', ''].join('\n'), + ) + } + + for (const relativePath of MANAGED_PROMPT_FILES) { + writeFile(repoRoot, relativePath, 'Use AGENTS.md for repo-wide rules.\n') + } + + writeFile(repoRoot, 'README.md', '# README\n') +} + +describe('check-doc-governance.mjs', () => { + afterEach(() => { + for (const tempDir of tempDirs) { + rmSync(tempDir, { recursive: true, force: true }) + tempDirs.delete(tempDir) + } + }) + + it('passes for a valid minimal fixture', () => { + const repoRoot = createTempRepo() + buildValidFixture(repoRoot) + + const failures = checkDocsGovernance(repoRoot) + + expect(failures).toEqual([]) + }) + + it('reports a missing pointer file without crashing', () => { + const repoRoot = createTempRepo() + buildValidFixture(repoRoot) + rmSync(path.join(repoRoot, 'CLAUDE.md')) + + const failures = checkDocsGovernance(repoRoot) + + expect(failures).toContain('Missing pointer file: CLAUDE.md') + expect(failures.join('\n')).not.toContain('ENOENT') + }) + + it('reports a missing adr directory without crashing', () => { + const repoRoot = createTempRepo() + buildValidFixture(repoRoot) + rmSync(path.join(repoRoot, 'docs/adr'), { recursive: true, force: true }) + + const failures = checkDocsGovernance(repoRoot) + + expect(failures).toContain('docs/adr must exist and be a directory') + expect(failures).toContain('Missing required canonical file: docs/adr/README.md') + expect(failures.join('\n')).not.toContain('ENOENT') + }) + + it('reports a missing canonical file without crashing on file-shape checks', () => { + const repoRoot = createTempRepo() + buildValidFixture(repoRoot) + rmSync(path.join(repoRoot, 'docs/ARCHITECTURE.md')) + + const failures = checkDocsGovernance(repoRoot) + + expect(failures).toContain('Missing required canonical file: docs/ARCHITECTURE.md') + expect(failures.join('\n')).not.toContain('ENOENT') + }) +})