code factory: add papercuts skill, greptile rules + greptile rules skill (#77)
* chore: add versioned Greptile review policy * chore: preserve review learnings and papercuts * docs: align Claude and agent guidance * docs: add shared engineering principles * docs: trim engineering principles
This commit is contained in:
parent
3f2b4872ca
commit
d7b52f0876
14
.agents/PAPERCUTS.md
Normal file
14
.agents/PAPERCUTS.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Papercuts
|
||||
|
||||
Small, non-blocking friction encountered while working in this repository. Log it in the moment; review and fix entries in a separate, user-requested cleanup pass.
|
||||
|
||||
This is not a completed-work log or a bug tracker. Never include secrets, credentials, personal data, or raw customer payloads.
|
||||
|
||||
## Open
|
||||
|
||||
- [ ] `2026-07-10T17:53:20Z` — `codex` — While validating `.greptile/`, both `pnpm exec prettier --check` and the existing `pnpm format:check` attempted to reconcile `node_modules` and aborted because no TTY was available. Calling `node_modules/.bin/prettier` performed the non-installing check successfully; the agent/CI path needs a stable way to run package scripts without an interactive modules purge.
|
||||
- [ ] `2026-07-10T18:12:35Z` — `codex` — While validating referenced files in zsh, using `path` as a loop variable overwrote zsh's special `path` array and made commands such as `git`, `jq`, and `sed` appear missing later in the same shell. Use a neutral name such as `file_path` in shell loops.
|
||||
|
||||
## Resolved
|
||||
|
||||
Move fixed entries here, mark them checked, and append the resolving date or commit.
|
||||
60
.agents/skills/maintain-greptile-rules/SKILL.md
Normal file
60
.agents/skills/maintain-greptile-rules/SKILL.md
Normal file
@ -0,0 +1,60 @@
|
||||
---
|
||||
name: maintain-greptile-rules
|
||||
description: Evaluate verified findings from merge-ready, Greptile, pull-request, CI, security, billing, and other code reviews, then promote durable review gaps into the version-controlled .greptile configuration. Use when a review uncovers a recurring or high-risk repository invariant that Greptile does not capture, when Greptile repeatedly produces a false positive, or when asked to audit or update OpenSEO's Greptile rules and context.
|
||||
---
|
||||
|
||||
# Maintain Greptile rules
|
||||
|
||||
Keep `.greptile/` high-signal. A review finding is evidence to evaluate, not automatically a new rule.
|
||||
|
||||
## 1. Verify the finding
|
||||
|
||||
- Reproduce or trace the finding against the current repository, including the relevant call path, tests, and intentional exceptions.
|
||||
- Read `.greptile/config.json`, `.greptile/rules.md`, and `.greptile/files.json` before proposing a change.
|
||||
- Distinguish a newly introduced risk from adjacent legacy debt. Do not encode an unverified assumption or a one-off implementation detail.
|
||||
- Repository absence is not policy evidence. If a reviewer infers a preference only because no current example exists, leave Greptile, code, and CI unchanged unless an owner explicitly adopts the policy.
|
||||
|
||||
## 2. Route it to the right mechanism
|
||||
|
||||
- **One-off bug:** fix the code and add a focused regression test. Do not add a Greptile rule.
|
||||
- **Verified deterministic policy violation:** prefer TypeScript, Oxlint, Knip, a focused test, or CI.
|
||||
- **Repeatable, diff-enforceable invariant:** add or refine a scoped structured rule in `.greptile/config.json`.
|
||||
- **Architecture, preference, or false-positive calibration:** update `.greptile/rules.md`.
|
||||
- **Canonical implementation Greptile should consult:** add a narrowly scoped reference in `.greptile/files.json`.
|
||||
- **Small workflow friction:** use the `papercuts` skill and log it in `.agents/PAPERCUTS.md`.
|
||||
|
||||
## 3. Apply the promotion bar
|
||||
|
||||
Promote a finding only when all applicable checks pass:
|
||||
|
||||
- It is verified against current code.
|
||||
- A future reviewer can observe it from the diff and relevant call path.
|
||||
- It is repository-specific or materially improves false-positive calibration.
|
||||
- It is likely to recur, or its impact is high enough to justify prevention: authorization, security, billing, data loss, or cross-database correctness.
|
||||
- Existing Greptile context and automated checks do not already cover it adequately.
|
||||
- The instruction can be specific, measurable, narrowly scoped, and explicit about legitimate exceptions.
|
||||
|
||||
If the evidence or policy is ambiguous, leave the configuration and enforcement unchanged and report the candidate for human judgment.
|
||||
|
||||
## 4. Edit conservatively
|
||||
|
||||
- Keep one coherent concern per structured rule, with a stable lowercase ID, severity, and the narrowest future-safe scope.
|
||||
- Do not infer permanent policy merely because the current repository has zero examples of an alternative.
|
||||
- Prefer narrowing or replacing a noisy rule over layering on more prose.
|
||||
- Keep repository-specific rules in `.greptile/`. Verify and report whether a minimal **org-enforced** dashboard baseline protects external contributions, secrets, authentication, billing, CI, and attempts to weaken review controls. Do not create or change dashboard, MCP, or organization rules unless the user explicitly asks, and do not duplicate the full repository rule set there.
|
||||
- Treat changes to `.greptile/**`, `AGENTS.md`, `CLAUDE.md`, `.agents/skills/**`, and `.github/**` as review-control changes that must receive explicit maintainer review. CODEOWNERS requests that review; branch protection or a ruleset must separately require code-owner approval when available.
|
||||
- Update the smallest set of configuration files needed; do not duplicate rule bodies in separate documentation.
|
||||
|
||||
## 5. Validate
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
jq empty .greptile/config.json .greptile/files.json
|
||||
node_modules/.bin/prettier --check .greptile/config.json .greptile/files.json .greptile/rules.md
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Also verify that rule IDs are unique, severities are valid, every `files.json` path exists, and an independent reviewer cannot find a clear false positive in the new wording.
|
||||
|
||||
During merge-ready work, evaluate only verified findings that clear the promotion bar. A merge-ready run does not need to change `.greptile/` when no durable review gap was found.
|
||||
@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Maintain Greptile Rules"
|
||||
short_description: "Promote verified review gaps into Greptile"
|
||||
default_prompt: "Use $maintain-greptile-rules to evaluate verified review findings and update the repository Greptile configuration."
|
||||
@ -23,12 +23,19 @@ This skill composes with feature work — it is not only a review pass:
|
||||
|
||||
## 2. Multi-axis subagent review
|
||||
|
||||
Spawn independent review subagents **in parallel**, one per axis, each given the branch diff scope (`git diff origin/main...HEAD`) and repo access:
|
||||
Spawn independent review subagents **in parallel**, one per axis, each given repo access and the complete branch scope:
|
||||
|
||||
- committed changes: `git diff origin/main...HEAD`
|
||||
- staged changes: `git diff --cached`
|
||||
- unstaged changes: `git diff`
|
||||
- untracked files: `git status --short`, followed by reading every in-scope untracked file
|
||||
|
||||
Do not let an uncommitted or newly created file escape review merely because it is absent from `origin/main...HEAD`.
|
||||
|
||||
1. **Unnecessary complexity** — thin wrappers, needless indirection, single-use abstractions, defensive guards for impossible states, dead config. This codebase deliberately stays simple.
|
||||
2. **Security** — authz on new endpoints (org/project scoping), SSRF, injection, secrets handling, anything user-input-shaped reaching D1/R2/external APIs.
|
||||
3. **Billing & metering** — ways a user could trigger DataForSEO/provider spend without being metered, charged-but-failed paths, retry/loop amplification, endpoints with unexpectedly high per-call user cost. Credits are billed via Autumn; uncounted spend is a revenue leak.
|
||||
4. **Library & project idioms** — TanStack (Router/Query/Start) used idiomatically; patterns match how the rest of the codebase already does it (Result-pattern error handling at provider seams, db/schema conventions, existing component patterns). Flag novel patterns where an established one exists.
|
||||
4. **Library & project idioms** — TanStack (Router/Query/Start) used idiomatically; patterns match how the rest of the codebase already does it (shared application/provider error boundaries, db/schema conventions, existing component patterns). Flag novel patterns where an established one exists.
|
||||
5. **Vibe-coded cruft** — leftover scaffolding, stale comments narrating the edit history, console.logs, TODO-without-owner, copy-pasted near-duplicates, files/exports nothing uses.
|
||||
|
||||
Each reviewer returns findings with file:line, severity (`blocker` / `should-fix` / `nitpick`), and a one-line rationale. Tell reviewers explicitly: this is an early-stage product — do not chase theoretical edge cases; mark anything debatable as `nitpick`.
|
||||
@ -37,6 +44,14 @@ Each reviewer returns findings with file:line, severity (`blocker` / `should-fix
|
||||
|
||||
For each `blocker` and `should-fix` finding, spawn verification subagents (in parallel) that adversarially check the finding against the actual code and verdict **APPLY / APPLY-MODIFIED / REJECT** with reasoning. Drop rejected findings. Nitpicks don't need verification — they're reported, not necessarily fixed.
|
||||
|
||||
### Preserve review learnings
|
||||
|
||||
After verification, route durable learnings without forcing every review to change policy:
|
||||
|
||||
- If an **APPLY** or **APPLY-MODIFIED** finding reveals a recurring or high-risk repository invariant that existing `.greptile/` context and CI do not capture, use `maintain-greptile-rules` and apply its promotion bar.
|
||||
- Keep one-off bugs as code fixes and regression tests. Put deterministic mechanical checks in CI or lint instead of Greptile.
|
||||
- When a small tooling, documentation, or workflow frustration occurs, use `papercuts` to append it to `.agents/PAPERCUTS.md`; do not derail merge-ready work to fix it.
|
||||
|
||||
## 4. Fix, check, loop
|
||||
|
||||
- Apply verified `blocker`/`should-fix` fixes. Apply nitpicks only when trivial and clearly right; otherwise list them in the PR for Ben to judge.
|
||||
|
||||
47
.agents/skills/papercuts/SKILL.md
Normal file
47
.agents/skills/papercuts/SKILL.md
Normal file
@ -0,0 +1,47 @@
|
||||
---
|
||||
name: papercuts
|
||||
description: Log small, non-blocking repository and agent-workflow friction to .agents/PAPERCUTS.md, or review, deduplicate, and resolve existing entries. Use proactively when a tool call has to be retried, setup or documentation is confusing, a command is flaky, a cache is stale, an error is misleading, or a non-obvious gotcha wastes time. Also use when the user asks to review, fix, or clean up papercuts.
|
||||
---
|
||||
|
||||
# Papercuts
|
||||
|
||||
Capture small friction in the moment without derailing the current task. Aggregated entries show where the repository needs sanding down.
|
||||
|
||||
## Decide whether it is a papercut
|
||||
|
||||
A papercut is small, non-blocking friction in the repository, tooling, documentation, or agent workflow that is easy to push through but likely to waste time again.
|
||||
|
||||
Do not log:
|
||||
|
||||
- A product or code correctness bug that should be fixed now or tracked as real work.
|
||||
- What the agent accomplished; that belongs in the task summary.
|
||||
- Secrets, credentials, personal data, raw customer payloads, or sensitive paths.
|
||||
- A one-off typo with no plausible recurrence.
|
||||
|
||||
## Log proactively
|
||||
|
||||
1. Search `.agents/PAPERCUTS.md` for an equivalent entry and avoid duplicates.
|
||||
2. Append one unchecked item under `## Open` using this format:
|
||||
|
||||
```markdown
|
||||
- [ ] `YYYY-MM-DDTHH:MM:SSZ` — `agent` — While <doing X>, <friction happened>. <Workaround, likely cause, or smallest useful improvement>.
|
||||
```
|
||||
|
||||
3. Keep it to one or two sentences: what you were doing, what got in the way, and optionally the likely fix.
|
||||
4. Continue the original task. Do not opportunistically expand a papercut into unrelated work.
|
||||
|
||||
Use UTC timestamps. Use a short agent label such as `codex`, `claude`, or `human` and include a PR or task identifier only when it helps future triage.
|
||||
|
||||
## Review or resolve
|
||||
|
||||
Only mine a whole session or perform a broad papercut review when the user explicitly asks. Proactive behavior is limited to logging friction as it occurs.
|
||||
|
||||
When asked to review the file:
|
||||
|
||||
1. Deduplicate and group related entries.
|
||||
2. Verify each open papercut still exists.
|
||||
3. Fix the smallest safe, high-leverage entries first.
|
||||
4. Move fixed items to `## Resolved`, check them, and append the resolving date or commit.
|
||||
5. Route real bugs to normal issue/fix work. Route recurring review-policy gaps through `maintain-greptile-rules` instead of turning them into papercuts.
|
||||
|
||||
Preserve useful history; do not delete resolved entries merely to make the file shorter.
|
||||
4
.agents/skills/papercuts/agents/openai.yaml
Normal file
4
.agents/skills/papercuts/agents/openai.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Papercuts"
|
||||
short_description: "Log and resolve recurring repository friction"
|
||||
default_prompt: "Use $papercuts to log, review, or resolve small recurring workflow friction in this repository."
|
||||
6
.github/CODEOWNERS
vendored
Normal file
6
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
# Request explicit maintainer review for review-control changes.
|
||||
/.github/ @bensenescu
|
||||
/.greptile/ @bensenescu
|
||||
/AGENTS.md @bensenescu
|
||||
/CLAUDE.md @bensenescu
|
||||
/.agents/skills/ @bensenescu
|
||||
136
.greptile/config.json
Normal file
136
.greptile/config.json
Normal file
@ -0,0 +1,136 @@
|
||||
{
|
||||
"strictness": 2,
|
||||
"commentTypes": ["logic", "syntax"],
|
||||
"triggerOnUpdates": true,
|
||||
"ignorePatterns": "src/routeTree.gen.ts\nweb/src/routeTree.gen.ts\nworker-configuration.d.ts\ndrizzle/meta/**\ndrizzle-pg/meta/**",
|
||||
"rules": [
|
||||
{
|
||||
"id": "tenant-resource-scope",
|
||||
"rule": "At each external trust boundary, establish the appropriate authorization before dispatch: session middleware for user endpoints, withMcpProjectAuth for project-scoped MCP handlers, signed state or verified provider signatures for callbacks and webhooks, and an explicit documented decision for public routes. Active project-scoped server functions use requireProjectContext with a validated projectId. Reads and mutations keyed by caller-controlled resource IDs must include the verified project, organization, or user in the query, or first authorize through a canonical parent lookup. Never trust a client-supplied organization, user, or billing identity. Archived or lifecycle resources may use requireAuthenticatedContext plus an explicit organization-scoped lookup. Bare-ID lookups are acceptable in trusted internal Workflow or Durable Object paths only when the upstream authorization invariant is explicit.",
|
||||
"scope": [
|
||||
"src/lib/auth*.ts",
|
||||
"src/server.ts",
|
||||
"src/server/features/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/server/mcp/**",
|
||||
"src/routes/api/**",
|
||||
"src/middleware/**",
|
||||
"web/src/routes/api/**"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "normalized-product-data",
|
||||
"rule": "Store relationships and independently queried, constrained, or evolving product concepts in normalized tables with foreign keys and join tables. Do not put relational IDs in JSON or delimited text to avoid joins. JSON or text is acceptable for opaque provider payloads, immutable history, caches, or bounded non-relational value arrays when the reason is clear.",
|
||||
"scope": [
|
||||
"src/db/**",
|
||||
"drizzle/**",
|
||||
"drizzle-pg/**",
|
||||
"src/server/**/repositories/**",
|
||||
"src/server/**/*Repository.ts"
|
||||
],
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"id": "dual-dialect-persistence",
|
||||
"rule": "A change to hand-authored application schema must update the SQLite and Postgres definitions and generated migrations for both providers. Preserve equivalent table, column, nullability, default, constraint, index, and foreign-key semantics while allowing intentional dialect-native representations and Better Auth exceptions. Queries, raw SQL, timestamp comparisons, conflict handling, and database-error classification must work on both providers or branch explicitly. Review provider-aware exports, schema-parity coverage, and D1-to-Postgres migration code when affected.",
|
||||
"scope": [
|
||||
"src/db/**",
|
||||
"drizzle/**",
|
||||
"drizzle-pg/**",
|
||||
"drizzle.config.ts",
|
||||
"drizzle-prod.config.ts",
|
||||
"drizzle-pg.config.ts",
|
||||
"src/lib/auth.ts",
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/middleware/**",
|
||||
"scripts/migrate-d1-to-postgres.ts"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "postgres-entrypoint-scope",
|
||||
"rule": "Every Worker, scheduled handler, Durable Object method or callback, or other entry point that can reach provider-aware Drizzle db from @/db without a guaranteed ambient request scope must establish withPgClient. Durable Object ctx.storage and framework-managed agent message persistence are not @/db access and do not need this wrapper. Every Cloudflare Workflow step uses pgStep; raw step.do is confined to the pgStep helper. Do not assume AsyncLocalStorage scope survives across Workflow steps or Durable Object callbacks.",
|
||||
"scope": [
|
||||
"src/server.ts",
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/routes/api/**",
|
||||
"src/middleware/**"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "atomic-multi-write",
|
||||
"rule": "When partial completion would violate an invariant, use runBatch and build every statement from its tx callback. executeInBatches is only for work where each committed chunk is independently safe or idempotent; it is not an all-or-nothing transaction. Hard concurrency or capacity admission must use a database constraint, transactional conditional write, or rollback-safe insert-first admission rather than count-then-act. Bound large inArray and bulk-value parameter lists for D1. Retryable Workflow writes need deterministic IDs, stable unique keys, or conflict-safe upserts.",
|
||||
"scope": [
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/middleware/**",
|
||||
"src/db/runBatch.ts"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "billable-dataforseo-seam",
|
||||
"rule": "Every billable hosted DataForSEO call uses createDataforseoClient with organization billing context. Preserve provider billing path and cost metadata when a billed response later fails parsing or validation so metering still occurs. Do not charge cache hits or provider-unbilled failures. Self-hosted calls, free location data, tests using SDK models, and queued task_get collection are intentional exceptions.",
|
||||
"scope": [
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**",
|
||||
"src/serverFunctions/**"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "billing-fails-closed",
|
||||
"rule": "Every billable hosted provider path must check organization credits before paid execution and meter provider-reported spend through the established shared credit-spend helper after execution. A failed gate prevents the paid call. A bounded partial-success API may surface a failed billing check as an explicit item-level error, but it must not present the paid operation as successful. Authorization failures terminate the request. Never use a stale-positive cache that can authorize access or spend that a live check would deny, and ensure retries or Workflow replays cannot omit metering or double-charge.",
|
||||
"scope": [
|
||||
"src/shared/billing*.ts",
|
||||
"src/shared/rank-tracking.ts",
|
||||
"src/server.ts",
|
||||
"src/server/billing/**",
|
||||
"src/server/lib/chatAgent.ts",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/lib/openrouter.ts",
|
||||
"src/server/lib/audit/lighthouse.ts",
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/routes/api/autumn/**"
|
||||
],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "untrusted-outbound-url",
|
||||
"rule": "Before fetching a user-derived initial target, call normalizeAndValidateStartUrl and use manual redirect handling. A redirect followed directly must be revalidated with normalizeAndValidateStartUrl before the next fetch. The audit crawler may record a redirect and enqueue its target instead; every discovered link, sitemap entry, or redirect target admitted to that crawl frontier must pass isCrawlableUrl plus the crawl's same-origin and robots policy before fetch. Never use automatic redirect following for an untrusted URL. Fixed provider URLs are exempt from SSRF screening but still follow their shared client's timeout, retry, and error policy.",
|
||||
"scope": ["src/server/**", "src/serverFunctions/**", "src/routes/api/**"],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "safe-external-links",
|
||||
"rule": "A clickable URL from API, crawl, LLM, or provider data must use getSafeExternalUrl, ExternalUrlCell, SafeExternalLink, or the shared Markdown and MARKDOWN_COMPONENTS renderer. Do not render untrusted values through a raw href or a new ad hoc scheme-check regex. Static developer-authored URLs are exempt.",
|
||||
"scope": ["src/client/**", "src/routes/**/*.tsx"],
|
||||
"severity": "high"
|
||||
},
|
||||
{
|
||||
"id": "behavior-evidence",
|
||||
"rule": "A change that alters authentication or authorization, billing or metering, persistence or query behavior, schema or migrations, provider serialization, Workflow retry or state transitions, or URL, search, and query behavior must include a focused behavioral test unless an existing test directly covers the changed branch or failure mode. A bug fix should reproduce the old failure. Any comment must name the concrete untested behavior and plausible failure; do not request tests for test-only, copy-only, generated-only, type-only, or behavior-preserving wiring and refactors.",
|
||||
"scope": [
|
||||
"src/**",
|
||||
"drizzle/**",
|
||||
"drizzle-pg/**",
|
||||
"drizzle.config.ts",
|
||||
"drizzle-prod.config.ts",
|
||||
"drizzle-pg.config.ts",
|
||||
"scripts/migrate-d1-to-postgres.ts"
|
||||
],
|
||||
"severity": "medium"
|
||||
}
|
||||
]
|
||||
}
|
||||
171
.greptile/files.json
Normal file
171
.greptile/files.json
Normal file
@ -0,0 +1,171 @@
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"path": "specs/0001-project-scoping-for-server-functions.md",
|
||||
"description": "Canonical server-function project-scoping design and exceptions.",
|
||||
"scope": [
|
||||
"src/serverFunctions/**",
|
||||
"src/server/mcp/**",
|
||||
"src/middleware/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/serverFunctions/middleware.ts",
|
||||
"description": "Established authenticated and active-project context middleware.",
|
||||
"scope": ["src/serverFunctions/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/server/mcp/project-auth.ts",
|
||||
"description": "Canonical project authorization wrapper for MCP handlers.",
|
||||
"scope": ["src/server/mcp/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/db/schema-parity.test.ts",
|
||||
"description": "Cross-dialect structural guarantees and mechanical DB guards. Coverage includes only explicitly registered schema modules, so verify that every affected module participates; passing this test does not prove semantic query or migration portability.",
|
||||
"scope": [
|
||||
"src/db/**",
|
||||
"drizzle/**",
|
||||
"drizzle-pg/**",
|
||||
"src/lib/auth.ts",
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/middleware/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/db/schema.ts",
|
||||
"description": "Provider-aware schema export surface for SQLite and Postgres.",
|
||||
"scope": [
|
||||
"src/db/**",
|
||||
"src/lib/auth.ts",
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/middleware/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "scripts/migrate-d1-to-postgres.ts",
|
||||
"description": "Operational D1-to-Postgres copy and transformation assumptions.",
|
||||
"scope": [
|
||||
"src/db/**",
|
||||
"drizzle/**",
|
||||
"drizzle-pg/**",
|
||||
"scripts/migrate-d1-to-postgres.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/db/runBatch.ts",
|
||||
"description": "Provider-aware atomic batch and chunked batch semantics.",
|
||||
"scope": ["src/server/**", "src/serverFunctions/**", "src/middleware/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/server/workflows/pgStep.ts",
|
||||
"description": "Required Postgres request scope for database-touching Workflow steps.",
|
||||
"scope": ["src/server/workflows/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/db/pg/client.ts",
|
||||
"description": "Request-scoped Postgres client and withPgClient contract for Worker and Durable Object entrypoints.",
|
||||
"scope": [
|
||||
"src/server.ts",
|
||||
"src/server/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/routes/api/**",
|
||||
"src/middleware/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/server/lib/dataforseo/client.ts",
|
||||
"description": "Single authenticated and metered DataForSEO consumer seam, including hosted and self-hosted behavior.",
|
||||
"scope": [
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**",
|
||||
"src/serverFunctions/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/server/lib/dataforseo/envelope.ts",
|
||||
"description": "Provider response validation and billed-failure metadata semantics. Known existing gap: parseTaskItems can currently discard billing metadata when a billed payload fails item validation; do not treat that path as precedent.",
|
||||
"scope": [
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/server/billing/subscription.ts",
|
||||
"description": "Canonical hosted credit checks, balance reads, spend ordering, and usage tracking.",
|
||||
"scope": [
|
||||
"src/shared/billing*.ts",
|
||||
"src/shared/rank-tracking.ts",
|
||||
"src/server.ts",
|
||||
"src/server/billing/**",
|
||||
"src/server/lib/chatAgent.ts",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/lib/openrouter.ts",
|
||||
"src/server/lib/audit/lighthouse.ts",
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/routes/api/autumn/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/server/billing/autumn.ts",
|
||||
"description": "Single lazy Autumn client and the restricted retry policy for non-idempotent track calls.",
|
||||
"scope": [
|
||||
"src/shared/billing*.ts",
|
||||
"src/shared/rank-tracking.ts",
|
||||
"src/server.ts",
|
||||
"src/server/billing/**",
|
||||
"src/server/lib/chatAgent.ts",
|
||||
"src/server/lib/dataforseoBillingClassification.ts",
|
||||
"src/server/lib/openrouter.ts",
|
||||
"src/server/lib/audit/lighthouse.ts",
|
||||
"src/server/lib/dataforseo/**",
|
||||
"src/server/features/**",
|
||||
"src/server/mcp/**",
|
||||
"src/server/workflows/**",
|
||||
"src/serverFunctions/**",
|
||||
"src/routes/api/autumn/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "src/server/lib/audit/url-policy.ts",
|
||||
"description": "Shared SSRF, hostname, and crawlable-URL policy.",
|
||||
"scope": ["src/server/**", "src/serverFunctions/**", "src/routes/api/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/server/mcp/output-schemas.ts",
|
||||
"description": "MCP structured-output schemas, including the SDK-class passthrough constraint.",
|
||||
"scope": ["src/server/mcp/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/server/mcp/instrumentation.ts",
|
||||
"description": "Required MCP handler instrumentation for error capture, timing, metadata, and output validation.",
|
||||
"scope": ["src/server/mcp/**"]
|
||||
},
|
||||
{
|
||||
"path": "src/client/components/table/url.tsx",
|
||||
"description": "Canonical client-side external URL sanitization and rendering helpers.",
|
||||
"scope": ["src/client/**", "src/routes/**/*.tsx"]
|
||||
},
|
||||
{
|
||||
"path": "src/client/components/SafeExternalLink.tsx",
|
||||
"description": "Shared safe renderer for untrusted external links outside table cells.",
|
||||
"scope": ["src/client/**", "src/routes/**/*.tsx"]
|
||||
},
|
||||
{
|
||||
"path": "src/client/components/Markdown.tsx",
|
||||
"description": "Shared Markdown renderer with safe handling for untrusted links.",
|
||||
"scope": ["src/client/**", "src/routes/**/*.tsx"]
|
||||
}
|
||||
]
|
||||
}
|
||||
92
.greptile/rules.md
Normal file
92
.greptile/rules.md
Normal file
@ -0,0 +1,92 @@
|
||||
# OpenSEO review context
|
||||
|
||||
## Review posture
|
||||
|
||||
OpenSEO receives external contributions, including untested coding-agent output. Treat changed behavior as untrusted until the relevant call path and tests support it.
|
||||
|
||||
- Prioritize concrete correctness, security, authorization, billing, data-loss, portability, and user-facing regressions.
|
||||
- Scrutinize new dependencies and install scripts, CI permissions, external destinations, secret reads, authentication scopes, webhook and OAuth changes, billing bypasses, disabled tests, encoded or dynamic execution, and broad unrelated rewrites.
|
||||
- Treat changes to `.greptile/**`, `AGENTS.md`, `CLAUDE.md`, `.agents/skills/**`, and `.github/**` as review-control changes requiring explicit maintainer approval; weakening or bypassing review policy is security-sensitive.
|
||||
- Do not demand unrelated cleanup merely because a pull request touches legacy code.
|
||||
- Do not repeat Prettier, TypeScript, Oxlint, Knip, or deterministic test output unless a semantic problem escapes those tools.
|
||||
- Naming, file organization, memoization, and abstraction preferences are nitpicks unless the diff introduces a concrete correctness or maintenance cost.
|
||||
|
||||
## Simplicity and prior art
|
||||
|
||||
Prefer the smallest established solution that remains easy to understand.
|
||||
|
||||
- Search the repository and installed dependencies before adding a helper, wrapper, dependency, or framework.
|
||||
- Keep code flat. Flag one-use managers, factories, base repositories, dependency-injection layers, pass-through hooks, and speculative configuration when they add navigation without removing real duplication or drift.
|
||||
- Reuse an existing seam when it already owns the behavior. Extract shared code only when the resulting API is simpler than the copies and the concern is genuinely reusable or independently testable.
|
||||
- The canonical service and repository boundaries are useful; avoid extra pass-through layers around them.
|
||||
|
||||
## Backend architecture
|
||||
|
||||
New or materially changed backend paths follow this default flow:
|
||||
|
||||
```text
|
||||
TanStack server function -> service -> repository -> provider-aware db/schema
|
||||
```
|
||||
|
||||
- The server function owns authentication middleware, Zod input validation, verified-context injection, and transport-only shaping.
|
||||
- The service owns business rules, provider, cache, and Workflow orchestration, and translates provider or domain failures into application errors when appropriate.
|
||||
- The repository owns Drizzle persistence and query behavior.
|
||||
- Do not put new database or provider orchestration directly in `src/serverFunctions/**`.
|
||||
- Do not create an empty repository for provider-only or pure-computation features.
|
||||
- A project-scoped MCP handler uses `withMcpProjectAuth`. Reuse an existing service when it implements the same capability; an MCP-only capability may call the shared authenticated and metered provider seam directly instead of adding a one-use service.
|
||||
- Register every MCP tool through `instrumentMcpToolHandler`; do not register a raw handler that bypasses shared error capture, timing, billing metadata, and output-schema validation.
|
||||
- Raw API routes, Worker dispatch, Durable Objects, Workflows, webhooks, and callbacks do not inherit server-function middleware; they establish and translate their own trust boundary explicitly.
|
||||
|
||||
Internal provider and domain code may use focused typed errors. Services translate provider or domain failures into application errors; server-function middleware and raw-route handlers own client-safe wire responses. A raw route may use shared `AppError` mapping or return an explicit non-sensitive `Response` appropriate to its protocol. Partial success is acceptable for explicitly independent items when failures remain visible; authorization, billing, validation, and required writes still fail closed.
|
||||
|
||||
## TypeScript and runtime validation
|
||||
|
||||
- Use idiomatic TypeScript and prefer `unknown` plus narrowing over `any`, unjustified assertions, or non-null assertions.
|
||||
- Validate untrusted server-function input and provider, webhook, cache, or browser-storage data whose fields affect behavior with Zod or a focused explicit predicate.
|
||||
- When a Zod schema defines a serialized contract crossing layers, derive its TypeScript type with `z.infer` instead of maintaining a parallel shape.
|
||||
- Strong library types and already-validated internal values do not require redundant parsing.
|
||||
- Reuse an installed library or established project helper instead of hand-rolling a parser, protocol, retry mechanism, URL validator, or state container.
|
||||
|
||||
For MCP output that passes external SDK class instances through `structuredContent`, follow `src/server/mcp/output-schemas.ts`: use `looseObjectOutputSchema`. `z.record` remains valid for ordinary plain-object maps.
|
||||
|
||||
## TanStack and React
|
||||
|
||||
- Use TanStack Query for ordinary server state and mutations. Query keys include project or tenant scope when the result is scoped, plus every semantic input that changes the result.
|
||||
- Prefix invalidation is intentional TanStack Query behavior; an invalidation key does not need to exactly equal every matching query key.
|
||||
- Use `enabled` or `skipToken` for missing prerequisites and inactive paid queries. Retry, focus-refetch, stale-time, and cache behavior must not cause accidental repeated spend.
|
||||
- Validate Router search parameters. When performing a partial search update, preserve unrelated sibling parameters. Put shareable and back/forward-sensitive page state in the URL; keep transient UI state and unapplied form drafts local.
|
||||
- TanStack Router loaders, `beforeLoad`, Suspense queries, and local `useState` are not categorically banned. Judge them by the established flow and the behavior they provide.
|
||||
- Prefer TanStack Form and shared form helpers for multi-field or validated submitted forms. Simple forms and transient validation may remain local when that is clearer.
|
||||
- Ordinary React hooks are unconditional; React 19's `use()` is the explicit exception and may appear in conditions or loops. Effects are for external synchronization, subscriptions, timers, measurement, or analytics, not a replacement for ordinary Query data fetching or render-derived state.
|
||||
|
||||
## Security boundaries
|
||||
|
||||
- Verify webhook signatures against the raw body with the established provider verifier before parsing or mutation; handle replays idempotently.
|
||||
- OAuth state must be signed, expiring, and callback-bound unless a vetted library such as Better Auth owns that invariant. Provider tokens remain encrypted at rest.
|
||||
- Read secret-bearing server runtime configuration through the runtime environment helpers or Workers bindings. Typed public/build-time `import.meta.env` values and build-mode checks such as `process.env.NODE_ENV` are accepted; never expose a secret through client or build-time environment APIs.
|
||||
- New outbound destinations, secret-bearing requests, auth changes, and billing changes require a manual security read of the changed path.
|
||||
|
||||
## False-positive controls
|
||||
|
||||
### Deployment modes
|
||||
|
||||
OpenSEO supports `hosted`, `cloudflare_access`, and `local_noauth` modes. `local_noauth` is an intentionally trusted local mode and is unsafe for public exposure; its lack of login is not automatically a vulnerability. Hosted mode uses Better Auth and organization-level Autumn billing. Self-hosted modes use the operator's provider key and intentionally bypass Autumn.
|
||||
|
||||
### Workspaces and fixtures
|
||||
|
||||
- `badseo/**` is a deliberately broken SEO fixture site. Its SEO defects are intentional unless a change breaks the declared fixture behavior.
|
||||
- `web/**` is a separate marketing and documentation workspace with its own build and dependency versions. Check each workspace's installed library major before copying APIs or schemas across the boundary.
|
||||
|
||||
### Generated and special-case files
|
||||
|
||||
- Do not request hand edits to generated route trees, `worker-configuration.d.ts`, or Drizzle metadata snapshots.
|
||||
- Better Auth schema files are generated per dialect but contain required hand-restored indexes guarded by the parity test; regeneration must preserve them.
|
||||
- Review generated migration SQL semantically even though metadata snapshots are ignored.
|
||||
|
||||
### Existing debt is not precedent
|
||||
|
||||
Some current files bypass the preferred layering, use manual frontend state patterns, or contain provider-specific assumptions. Do not copy those exceptions into new code, but do not request unrelated refactors. Comment only when the contribution introduces, expands, or depends on the risky behavior.
|
||||
|
||||
SQLite and Postgres hand-authored timestamps are text, but their database defaults are not byte-identical: SQLite uses a space-separated value while Postgres uses ISO text. Do not enforce a false rule that every stored timestamp is ISO; review comparisons, writes, and migrations against the active provider's format.
|
||||
|
||||
`parseTaskItems` in the current DataForSEO envelope can lose billing metadata when a provider-billed payload later fails item validation. This is known debt, not a safe error-handling precedent. Comment when a contribution introduces, expands, or depends on that behavior; do not request an unrelated cleanup in other changes.
|
||||
25
AGENTS.md
Normal file
25
AGENTS.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Agent guidance
|
||||
|
||||
## Engineering principles
|
||||
|
||||
- Prefer simple, readable, flat code with minimal indirection.
|
||||
- Search for existing implementations and installed libraries before creating new helpers or abstractions.
|
||||
- Abstract when it prevents meaningful drift and makes the result simpler to maintain. Avoid speculative or one-use abstraction layers.
|
||||
- Keep product data normalized and relationships explicit. Do not encode relational data in JSON or text merely to avoid joins.
|
||||
- For new application-backed backend functionality, default to: TanStack server function → service → repository.
|
||||
- Keep schema changes, queries, and mutations compatible with both SQLite and Postgres.
|
||||
- Use idiomatic TypeScript. Use Zod to validate untrusted data and narrow runtime values at trust boundaries.
|
||||
- Prefer established project helpers and libraries over hand-rolled implementations.
|
||||
- Prefer idiomatic TanStack Query, Router, and Form patterns for server state, routing, and submitted forms.
|
||||
|
||||
## Log papercuts
|
||||
|
||||
When small, non-blocking repository friction occurs—a retried tool call, confusing setup step, flaky command, stale cache, misleading error, or non-obvious gotcha—use the `papercuts` skill and append it to `.agents/PAPERCUTS.md` in the moment. Continue the current task. Real bugs and tracked work are not papercuts, and sensitive data must never be logged.
|
||||
|
||||
Do not mine an entire session for papercuts or start a broad cleanup unless the user explicitly asks.
|
||||
|
||||
## Preserve review learnings
|
||||
|
||||
After a merge-ready or other code review verifies a finding, use `maintain-greptile-rules` only when the finding exposes a recurring or high-risk repository invariant that existing `.greptile/` context and automated checks do not capture. Do not promote one-off bugs or preferences into permanent review rules.
|
||||
|
||||
Changes to `.greptile/**`, `AGENTS.md`, `CLAUDE.md`, `.agents/skills/**`, and `.github/**` alter the review control plane and must receive explicit maintainer review. CODEOWNERS requests that review; where repository settings allow, enable GitHub's requirement for code-owner approval. Repository-specific rules live in `.greptile/`; maintainers should configure or retain a minimal org-enforced Greptile baseline for external-contribution, secret, authentication, billing, CI, and rule-tampering risks. Agents should report an unverified or missing baseline and must not mutate dashboard or organization rules without explicit user authorization.
|
||||
25
CLAUDE.md
Normal file
25
CLAUDE.md
Normal file
@ -0,0 +1,25 @@
|
||||
# Agent guidance
|
||||
|
||||
## Engineering principles
|
||||
|
||||
- Prefer simple, readable, flat code with minimal indirection.
|
||||
- Search for existing implementations and installed libraries before creating new helpers or abstractions.
|
||||
- Abstract when it prevents meaningful drift and makes the result simpler to maintain. Avoid speculative or one-use abstraction layers.
|
||||
- Keep product data normalized and relationships explicit. Do not encode relational data in JSON or text merely to avoid joins.
|
||||
- For new application-backed backend functionality, default to: TanStack server function → service → repository.
|
||||
- Keep schema changes, queries, and mutations compatible with both SQLite and Postgres.
|
||||
- Use idiomatic TypeScript. Use Zod to validate untrusted data and narrow runtime values at trust boundaries.
|
||||
- Prefer established project helpers and libraries over hand-rolled implementations.
|
||||
- Prefer idiomatic TanStack Query, Router, and Form patterns for server state, routing, and submitted forms.
|
||||
|
||||
## Log papercuts
|
||||
|
||||
When small, non-blocking repository friction occurs—a retried tool call, confusing setup step, flaky command, stale cache, misleading error, or non-obvious gotcha—use the `papercuts` skill and append it to `.agents/PAPERCUTS.md` in the moment. Continue the current task. Real bugs and tracked work are not papercuts, and sensitive data must never be logged.
|
||||
|
||||
Do not mine an entire session for papercuts or start a broad cleanup unless the user explicitly asks.
|
||||
|
||||
## Preserve review learnings
|
||||
|
||||
After a merge-ready or other code review verifies a finding, use `maintain-greptile-rules` only when the finding exposes a recurring or high-risk repository invariant that existing `.greptile/` context and automated checks do not capture. Do not promote one-off bugs or preferences into permanent review rules.
|
||||
|
||||
Changes to `.greptile/**`, `AGENTS.md`, `CLAUDE.md`, `.agents/skills/**`, and `.github/**` alter the review control plane and must receive explicit maintainer review. CODEOWNERS requests that review; where repository settings allow, enable GitHub's requirement for code-owner approval. Repository-specific rules live in `.greptile/`; maintainers should configure or retain a minimal org-enforced Greptile baseline for external-contribution, secret, authentication, billing, CI, and rule-tampering risks. Agents should report an unverified or missing baseline and must not mutate dashboard or organization rules without explicit user authorization.
|
||||
Loading…
x
Reference in New Issue
Block a user