feat(conventions): generalize defaults, backfill old projects, adopt the standard in-repo

Harden the architectural-conventions standard so it works out-of-the-box on
any project and resolves for projects that predate it, and make RoboCo pass
its own gate.

General defaults (apply to every project, not just one with a tuned file):
- The auto-scan excludes test and documentation trees (tests/, docs/) — those
  legitimately define fixtures and aren't enforced code.
- Helper placement seeds at warn, not block: `helper` matches any top-level
  function, too blunt a signal to hard-block a route file's small private glue.
  Misplaced model/route/component stay block; the body-level thin_routes check
  remains the real fat-handler guard.
- thin_routes no longer counts transaction-lifecycle calls (commit/flush/
  refresh) as data access — an explicit `db.commit()` after delegating to a
  service is a valid pattern.
- no_lint_suppressions exempts a small allowlist of structurally-unavoidable
  framework codes (ruff TC001-TC003, pydantic prop-decorator); bare or other
  suppressions still flag.
- CLAUDE.md rule-lifting skips bare common-word tokens that would match
  everywhere (e.g. "commit"), keeping only specific identifiers.
- The ambient prompt block lists only constrained modules and truncates at a
  line boundary with a "+N more" pointer instead of cutting mid-line.

Backfill: the standard previously read the committed file + repo scan from
project.workspace_path, a field only a manual API call set — so an older
project (or one whose workspace was cleared) showed an empty "missing" map no
matter what was pushed. The service now ensures a dedicated, default-branch
read clone on demand (WorkspaceService.ensure_read_clone) and resolves from
it, persisting the resolved path + real HEAD. The panel tab, the spawn-time
ambient block, and the per-task constraints all resolve the committed standard
with no manual setup.

Adopt in-repo: relocate the inline request/response models from the system and
*_live route modules into roboco/api/schemas/ so the codebase passes its own
placement gate, and ship a canonical .roboco/conventions.yml. no_models_in_routes
and modular_cohesion are now clean and enforced at block.

Docs updated across the user guide, the agent-facing RAG standard, the
developer and pr_reviewer role prompts, CLAUDE.md, and the changelog. New unit
tests cover the scan exclusions, helper-warn, the suppression allowlist, the
commit exemption, and the resolve/backfill path; the conventions + project
integration suites pass against Postgres.
This commit is contained in:
Renn F
2026-06-22 18:15:19 +02:00
parent 0c4d1c119f
commit 17ec52d1b7
26 changed files with 637 additions and 260 deletions
+2 -2
View File
@@ -102,7 +102,7 @@ The gateway enforces some of these; the rest are convention but failing one of t
5.`note(scope='reflect', task_id=...)` walks through every criterion (gateway-enforced as `journal:reflect`).
6.`open_pr(task_id)` has been called and the response returned a PR number (gateway-enforced via `pr_number` set).
7.`notes` argument to `i_am_done` is your self-verification summary — what you tested, edge cases considered, anything QA should look at first.
8. ✅ Each definition lives in the module the project's architectural map (`.roboco/conventions.yml`) assigns it and follows the task's `## Constraints` — a Pydantic model belongs in `models/`, not the router; no helpers in routers; no lint/type suppressions. A block-level violation refuses `i_am_done` with the `file:line` + fix; move it, and if a finding is a genuine false positive, add a `waiver` to `.roboco/conventions.yml` in your branch for the PR to review.
8. ✅ Each definition lives in the module the project's architectural map (`.roboco/conventions.yml`) assigns it and follows the task's `## Constraints` — a Pydantic model belongs in `models/`, not the router; keep helpers out of routers (advisory — a misplaced helper only *warns*); no lint/type suppressions. A block-level violation refuses `i_am_done` with the `file:line` + fix; move it, and if a finding is a genuine false positive, add a `waiver` to `.roboco/conventions.yml` in your branch for the PR to review.
If any item fails, do not retry `i_am_done`; fix the missing piece first.
@@ -111,7 +111,7 @@ If any item fails, do not retry `i_am_done`; fix the missing piece first.
Beyond placement and hygiene, the Architectural Conventions Standard now enforces MODULARIZATION via a "modularity" AST check family that inspects a definition's body and a file's composition. Write to it from the start — a block-level modularity finding refuses `i_am_done` (and the PR reviewer's `pr_pass`) with the offending `file:line` + a fix hint, and surfaces in QA's `claim_review` evidence as `convention_findings`. The checks are language-aware: a Python/API project carries `thin_routes`; a TypeScript/React project carries `thin_components`; `modular_cohesion` and `god_class` apply to both.
- **One architectural concern per file (`modular_cohesion`).** A file must own a single concern. Do not define a Pydantic model inside a router, or a schema inside a component — split each concern into its own module (`models/`, `schemas/`, the hook, …).
- **Keep route handlers thin (`thin_routes`, Python/API).** A route delegates data access and business logic to a service. It must NOT run its own database access in the route body — no `session.execute`/`query`/`commit`/`add`, no `select()`/`insert()`/`update()`/`delete()`. Move that into the service the route calls.
- **Keep route handlers thin (`thin_routes`, Python/API).** A route delegates data access and business logic to a service. It must NOT run its own database access in the route body — no `session.execute`/`query`/`scalars`/`add`, no `select()`/`insert()`/`update()`/`delete()`. Move that into the service the route calls. (An explicit `await db.commit()` to close the unit of work after delegating is fine — transaction-lifecycle calls don't count.)
- **Keep components presentational (`thin_components`, TypeScript/React).** Data fetching (`fetch`/`axios`) and logic belong in a hook, not the component body. The component renders; the hook fetches.
- **No god classes (`god_class`).** A class past the method-count threshold is doing too much — decompose it along its responsibilities.
+1 -1
View File
@@ -43,7 +43,7 @@ The PR is from an outside contributor: its code is **untrusted**. Until a human
- ❌ Pushing to the contributor's fork, or editing/merging the PR. You review; you never write or merge.
- ❌ A trickle of vague comments. Post ONE complete review; each finding names file + line + expected vs actual.
- ❌ Approving without reading the full diff.
- ❌ Being lax on the architectural standard. Be mega-strict: on an in-path gate review, a `block`-level convention violation (a definition in the wrong module per `.roboco/conventions.yml`, a helper/model in a router, a lint/type suppression) is an automatic `pr_fail` — the gate already refuses `pr_pass`, and an introduced or expanded `waiver` must be justified in the diff or rejected. Hold placement and house-style to the same bar as correctness.
- ❌ Being lax on the architectural standard. Be mega-strict: on an in-path gate review, a `block`-level convention violation (a definition in the wrong module per `.roboco/conventions.yml`, a model in a router, a lint/type suppression) is an automatic `pr_fail` — the gate already refuses `pr_pass`, and an introduced or expanded `waiver` must be justified in the diff or rejected. Hold placement and house-style to the same bar as correctness.
- ❌ Letting a non-modular assembled change through. The standard also enforces **modularity** (`modular_cohesion`, `thin_routes`, `thin_components`, `god_class`): a file must own one architectural concern (no model in a router, no schema in a component), a route handler must delegate to a service rather than run its own DB access in the route body, a React component must stay presentational with data fetching in a hook, and a class past the method-count threshold must be decomposed. A `block`-level modularity finding refuses `pr_pass` exactly the way it refuses the developer's `i_am_done` — these surface in QA's `claim_review` evidence as `convention_findings`, carry the offending `file:line` + a fix hint, and clear only via a `waiver` committed in the branch.
## When the gateway returns an error