automatically reorder sections on agents.md by priority

This commit is contained in:
Pedro Rodrigues
2026-01-22 08:48:47 +00:00
parent a1fbd23d4b
commit 7b1a65007b
7 changed files with 238 additions and 194 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ dist/
.DS_Store
.claude
.vscode

View File

@@ -89,6 +89,12 @@ npm run validate # Check rule format
npm run build # Generate AGENTS.md
```
**Automatic Section Ordering**: The build system automatically reads section order
from `_sections.md`. To reorder sections (e.g., promoting Security from MEDIUM-HIGH
to CRITICAL priority), simply edit the section numbers in `_sections.md` and
rebuild. The section mapping is generated dynamically, so manual updates to
`config.ts` are no longer needed.
### Impact Levels
| Level | Improvement | Examples |

View File

@@ -18,6 +18,10 @@ function parseSections(): Section[] {
const content = readFileSync(sectionsFile, "utf-8");
const sections: Section[] = [];
// Match format: Impact and Description on separate lines
// ## 1. Query Performance (query)
// **Impact:** CRITICAL
// **Description:** Description text
const sectionMatches = content.matchAll(
/##\s+(\d+)\.\s+([^\n(]+)\s*\((\w+)\)\s*\n\*\*Impact:\*\*\s*(\w+(?:-\w+)?)\s*\n\*\*Description:\*\*\s*([^\n]+)/g,
);
@@ -56,25 +60,25 @@ function getDefaultSections(): Section[] {
},
{
number: 3,
title: "Security & RLS",
prefix: "security",
impact: "CRITICAL",
description: "Row-Level Security, privileges, auth patterns",
},
{
number: 4,
title: "Schema Design",
prefix: "schema",
impact: "HIGH",
description: "Table design, indexes, partitioning, data types",
},
{
number: 4,
number: 5,
title: "Concurrency & Locking",
prefix: "lock",
impact: "MEDIUM-HIGH",
description: "Transactions, isolation, deadlocks",
},
{
number: 5,
title: "Security & RLS",
prefix: "security",
impact: "MEDIUM-HIGH",
description: "Row-Level Security, privileges, auth patterns",
},
{
number: 6,
title: "Data Access Patterns",
@@ -129,6 +133,19 @@ function toAnchor(text: string): string {
.replace(/\s+/g, "-");
}
/**
* Generate SECTION_MAP from parsed sections
*/
export function generateSectionMap(
sections: Section[],
): Record<string, number> {
const map: Record<string, number> = {};
for (const section of sections) {
map[section.prefix] = section.number;
}
return map;
}
/**
* Build AGENTS.md from all rule files
*/
@@ -138,6 +155,7 @@ function buildAgents(): void {
// Load metadata and sections
const metadata = loadMetadata();
const sections = parseSections();
const sectionMap = generateSectionMap(sections);
// Get all rule files
const ruleFiles = readdirSync(RULES_DIR)
@@ -152,7 +170,7 @@ function buildAgents(): void {
const rules: Rule[] = [];
for (const file of ruleFiles) {
const validation = validateRuleFile(file);
const validation = validateRuleFile(file, sectionMap);
if (!validation.valid) {
console.error(`Skipping invalid file ${basename(file)}:`);
for (const e of validation.errors) {
@@ -161,7 +179,7 @@ function buildAgents(): void {
continue;
}
const result = parseRuleFile(file);
const result = parseRuleFile(file, sectionMap);
if (result.success && result.rule) {
rules.push(result.rule);
}
@@ -303,4 +321,4 @@ if (isMainModule) {
buildAgents();
}
export { buildAgents };
export { buildAgents, parseSections };

View File

@@ -20,14 +20,17 @@ export const RULES_DIR = join(SKILL_DIR, "rules");
export const AGENTS_OUTPUT = join(SKILL_DIR, "AGENTS.md");
export const METADATA_FILE = join(SKILL_DIR, "metadata.json");
// Section prefix to number mapping
// Section prefix to number mapping (DEPRECATED)
// This is kept as a fallback, but the build system now generates
// the section map dynamically from _sections.md.
// To reorder sections, simply change the order in _sections.md.
export const SECTION_MAP: Record<string, number> = {
query: 1,
conn: 2,
connection: 2,
schema: 3,
lock: 4,
security: 5,
security: 3,
schema: 4,
lock: 5,
data: 6,
monitor: 7,
advanced: 8,

View File

@@ -1,6 +1,6 @@
import { readFileSync } from "node:fs";
import { basename } from "node:path";
import { IMPACT_LEVELS, SECTION_MAP } from "./config.js";
import { IMPACT_LEVELS } from "./config.js";
import type { CodeExample, ImpactLevel, ParseResult, Rule } from "./types.js";
/**
@@ -48,10 +48,13 @@ function parseFrontmatter(content: string): {
/**
* Extract section number from filename prefix
*/
function getSectionFromFilename(filename: string): number | null {
function getSectionFromFilename(
filename: string,
sectionMap: Record<string, number>,
): number | null {
const base = basename(filename, ".md");
const prefix = base.split("-")[0];
return SECTION_MAP[prefix] ?? null;
return sectionMap[prefix] ?? null;
}
/**
@@ -207,7 +210,10 @@ function extractReferences(body: string): string[] {
/**
* Parse a rule file and return structured data
*/
export function parseRuleFile(filePath: string): ParseResult {
export function parseRuleFile(
filePath: string,
sectionMap: Record<string, number>,
): ParseResult {
const errors: string[] = [];
const warnings: string[] = [];
@@ -216,7 +222,7 @@ export function parseRuleFile(filePath: string): ParseResult {
const { frontmatter, body } = parseFrontmatter(content);
// Extract section from filename
const section = getSectionFromFilename(filePath);
const section = getSectionFromFilename(filePath, sectionMap);
if (section === null) {
errors.push(
`Could not determine section from filename: ${basename(filePath)}`,

View File

@@ -1,5 +1,6 @@
import { readdirSync } from "node:fs";
import { basename, join } from "node:path";
import { generateSectionMap, parseSections } from "./build.js";
import { IMPACT_LEVELS, RULES_DIR } from "./config.js";
import { parseRuleFile } from "./parser.js";
import type { ValidationResult } from "./types.js";
@@ -34,11 +35,20 @@ function isGoodExample(label: string): boolean {
/**
* Validate a single rule file
*/
export function validateRuleFile(filePath: string): ValidationResult {
export function validateRuleFile(
filePath: string,
sectionMap?: Record<string, number>,
): ValidationResult {
const errors: string[] = [];
const warnings: string[] = [];
const result = parseRuleFile(filePath);
// Generate section map if not provided
if (!sectionMap) {
const sections = parseSections();
sectionMap = generateSectionMap(sections);
}
const result = parseRuleFile(filePath, sectionMap);
// Add parser errors and warnings
errors.push(...result.errors);

View File

@@ -30,22 +30,22 @@ Comprehensive Postgres performance optimization guide for developers using Supab
- 2.4 [Use Prepared Statements Correctly with Pooling](#24-use-prepared-statements-correctly-with-pooling)
3. [Security & RLS](#security-rls) - **CRITICAL**
- 3.1 [Choose Appropriate Data Types](#31-choose-appropriate-data-types)
- 3.2 [Index Foreign Key Columns](#32-index-foreign-key-columns)
- 3.3 [Partition Large Tables for Better Performance](#33-partition-large-tables-for-better-performance)
- 3.4 [Select Optimal Primary Key Strategy](#34-select-optimal-primary-key-strategy)
- 3.5 [Use Lowercase Identifiers for Compatibility](#35-use-lowercase-identifiers-for-compatibility)
- 3.1 [Apply Principle of Least Privilege](#31-apply-principle-of-least-privilege)
- 3.2 [Enable Row Level Security for Multi-Tenant Data](#32-enable-row-level-security-for-multi-tenant-data)
- 3.3 [Optimize RLS Policies for Performance](#33-optimize-rls-policies-for-performance)
4. [Schema Design](#schema-design) - **HIGH**
- 4.1 [Keep Transactions Short to Reduce Lock Contention](#41-keep-transactions-short-to-reduce-lock-contention)
- 4.2 [Prevent Deadlocks with Consistent Lock Ordering](#42-prevent-deadlocks-with-consistent-lock-ordering)
- 4.3 [Use Advisory Locks for Application-Level Locking](#43-use-advisory-locks-for-application-level-locking)
- 4.4 [Use SKIP LOCKED for Non-Blocking Queue Processing](#44-use-skip-locked-for-non-blocking-queue-processing)
- 4.1 [Choose Appropriate Data Types](#41-choose-appropriate-data-types)
- 4.2 [Index Foreign Key Columns](#42-index-foreign-key-columns)
- 4.3 [Partition Large Tables for Better Performance](#43-partition-large-tables-for-better-performance)
- 4.4 [Select Optimal Primary Key Strategy](#44-select-optimal-primary-key-strategy)
- 4.5 [Use Lowercase Identifiers for Compatibility](#45-use-lowercase-identifiers-for-compatibility)
5. [Concurrency & Locking](#concurrency-locking) - **MEDIUM-HIGH**
- 5.1 [Apply Principle of Least Privilege](#51-apply-principle-of-least-privilege)
- 5.2 [Enable Row Level Security for Multi-Tenant Data](#52-enable-row-level-security-for-multi-tenant-data)
- 5.3 [Optimize RLS Policies for Performance](#53-optimize-rls-policies-for-performance)
- 5.1 [Keep Transactions Short to Reduce Lock Contention](#51-keep-transactions-short-to-reduce-lock-contention)
- 5.2 [Prevent Deadlocks with Consistent Lock Ordering](#52-prevent-deadlocks-with-consistent-lock-ordering)
- 5.3 [Use Advisory Locks for Application-Level Locking](#53-use-advisory-locks-for-application-level-locking)
- 5.4 [Use SKIP LOCKED for Non-Blocking Queue Processing](#54-use-skip-locked-for-non-blocking-queue-processing)
6. [Data Access Patterns](#data-access-patterns) - **MEDIUM**
- 6.1 [Batch INSERT Statements for Bulk Data](#61-batch-insert-statements-for-bulk-data)
@@ -433,7 +433,155 @@ Reference: https://supabase.com/docs/guides/database/connecting-to-postgres#conn
Row-Level Security policies, privilege management, and authentication patterns.
### 3.1 Choose Appropriate Data Types
### 3.1 Apply Principle of Least Privilege
**Impact: MEDIUM (Reduced attack surface, better audit trail)**
Grant only the minimum permissions required. Never use superuser for application queries.
**Incorrect (overly broad permissions):**
```sql
-- Application uses superuser connection
-- Or grants ALL to application role
grant all privileges on all tables in schema public to app_user;
grant all privileges on all sequences in schema public to app_user;
-- Any SQL injection becomes catastrophic
-- drop table users; cascades to everything
```
**Correct (minimal, specific grants):**
```sql
-- Create role with no default privileges
create role app_readonly nologin;
-- Grant only SELECT on specific tables
grant usage on schema public to app_readonly;
grant select on public.products, public.categories to app_readonly;
-- Create role for writes with limited scope
create role app_writer nologin;
grant usage on schema public to app_writer;
grant select, insert, update on public.orders to app_writer;
grant usage on sequence orders_id_seq to app_writer;
-- No DELETE permission
-- Login role inherits from these
create role app_user login password 'xxx';
grant app_writer to app_user;
-- Revoke default public access
revoke all on schema public from public;
revoke all on all tables in schema public from public;
```
Revoke public defaults:
Reference: https://supabase.com/blog/postgres-roles-and-privileges
---
### 3.2 Enable Row Level Security for Multi-Tenant Data
**Impact: CRITICAL (Database-enforced tenant isolation, prevent data leaks)**
Row Level Security (RLS) enforces data access at the database level, ensuring users only see their own data.
**Incorrect (application-level filtering only):**
```sql
-- Relying only on application to filter
select * from orders where user_id = $current_user_id;
-- Bug or bypass means all data is exposed!
select * from orders; -- Returns ALL orders
```
**Correct (database-enforced RLS):**
```sql
-- Enable RLS on the table
alter table orders enable row level security;
-- Create policy for users to see only their orders
create policy orders_user_policy on orders
for all
using (user_id = current_setting('app.current_user_id')::bigint);
-- Force RLS even for table owners
alter table orders force row level security;
-- Set user context and query
set app.current_user_id = '123';
select * from orders; -- Only returns orders for user 123
create policy orders_user_policy on orders
for all
to authenticated
using (user_id = auth.uid());
```
Policy for authenticated role:
Reference: https://supabase.com/docs/guides/database/postgres/row-level-security
---
### 3.3 Optimize RLS Policies for Performance
**Impact: HIGH (5-10x faster RLS queries with proper patterns)**
Poorly written RLS policies can cause severe performance issues. Use subqueries and indexes strategically.
**Incorrect (function called for every row):**
```sql
create policy orders_policy on orders
using (auth.uid() = user_id); -- auth.uid() called per row!
-- With 1M rows, auth.uid() is called 1M times
```
**Correct (wrap functions in SELECT):**
```sql
create policy orders_policy on orders
using ((select auth.uid()) = user_id); -- Called once, cached
-- 100x+ faster on large tables
-- Create helper function (runs as definer, bypasses RLS)
create or replace function is_team_member(team_id bigint)
returns boolean
language sql
security definer
set search_path = ''
as $$
select exists (
select 1 from public.team_members
where team_id = $1 and user_id = (select auth.uid())
);
$$;
-- Use in policy (indexed lookup, not per-row check)
create policy team_orders_policy on orders
using ((select is_team_member(team_id)));
create index orders_user_id_idx on orders (user_id);
```
Use security definer functions for complex checks:
Always add indexes on columns used in RLS policies:
Reference: https://supabase.com/docs/guides/database/postgres/row-level-security#rls-performance-recommendations
---
## 4. Schema Design
**Impact: HIGH**
Table design, index strategies, partitioning, and data type selection. Foundation for long-term performance.
### 4.1 Choose Appropriate Data Types
**Impact: HIGH (50% storage reduction, faster comparisons)**
@@ -474,7 +622,7 @@ Reference: https://www.postgresql.org/docs/current/datatype.html
---
### 3.2 Index Foreign Key Columns
### 4.2 Index Foreign Key Columns
**Impact: HIGH (10-100x faster JOINs and CASCADE operations)**
@@ -528,7 +676,7 @@ Reference: https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONS
---
### 3.3 Partition Large Tables for Better Performance
### 4.3 Partition Large Tables for Better Performance
**Impact: MEDIUM-HIGH (5-20x faster queries and maintenance on large tables)**
@@ -580,7 +728,7 @@ Reference: https://www.postgresql.org/docs/current/ddl-partitioning.html
---
### 3.4 Select Optimal Primary Key Strategy
### 4.4 Select Optimal Primary Key Strategy
**Impact: HIGH (Better index locality, reduced fragmentation)**
@@ -636,7 +784,7 @@ Guidelines:
---
### 3.5 Use Lowercase Identifiers for Compatibility
### 4.5 Use Lowercase Identifiers for Compatibility
**Impact: MEDIUM (Avoid case-sensitivity bugs with tools, ORMs, and AI assistants)**
@@ -686,13 +834,13 @@ Reference: https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-S
---
## 4. Schema Design
## 5. Concurrency & Locking
**Impact: HIGH**
**Impact: MEDIUM-HIGH**
Table design, index strategies, partitioning, and data type selection. Foundation for long-term performance.
Transaction management, isolation levels, deadlock prevention, and lock contention patterns.
### 4.1 Keep Transactions Short to Reduce Lock Contention
### 5.1 Keep Transactions Short to Reduce Lock Contention
**Impact: MEDIUM-HIGH (3-5x throughput improvement, fewer deadlocks)**
@@ -737,7 +885,7 @@ Reference: https://www.postgresql.org/docs/current/tutorial-transactions.html
---
### 4.2 Prevent Deadlocks with Consistent Lock Ordering
### 5.2 Prevent Deadlocks with Consistent Lock Ordering
**Impact: MEDIUM-HIGH (Eliminate deadlock errors, improve reliability)**
@@ -794,7 +942,7 @@ Detect deadlocks in logs:
---
### 4.3 Use Advisory Locks for Application-Level Locking
### 5.3 Use Advisory Locks for Application-Level Locking
**Impact: MEDIUM (Efficient coordination without row-level lock overhead)**
@@ -845,7 +993,7 @@ Reference: https://www.postgresql.org/docs/current/explicit-locking.html#ADVISOR
---
### 4.4 Use SKIP LOCKED for Non-Blocking Queue Processing
### 5.4 Use SKIP LOCKED for Non-Blocking Queue Processing
**Impact: MEDIUM-HIGH (10x throughput for worker queues)**
@@ -894,154 +1042,6 @@ Reference: https://www.postgresql.org/docs/current/sql-select.html#SQL-FOR-UPDAT
---
## 5. Concurrency & Locking
**Impact: MEDIUM-HIGH**
Transaction management, isolation levels, deadlock prevention, and lock contention patterns.
### 5.1 Apply Principle of Least Privilege
**Impact: MEDIUM (Reduced attack surface, better audit trail)**
Grant only the minimum permissions required. Never use superuser for application queries.
**Incorrect (overly broad permissions):**
```sql
-- Application uses superuser connection
-- Or grants ALL to application role
grant all privileges on all tables in schema public to app_user;
grant all privileges on all sequences in schema public to app_user;
-- Any SQL injection becomes catastrophic
-- drop table users; cascades to everything
```
**Correct (minimal, specific grants):**
```sql
-- Create role with no default privileges
create role app_readonly nologin;
-- Grant only SELECT on specific tables
grant usage on schema public to app_readonly;
grant select on public.products, public.categories to app_readonly;
-- Create role for writes with limited scope
create role app_writer nologin;
grant usage on schema public to app_writer;
grant select, insert, update on public.orders to app_writer;
grant usage on sequence orders_id_seq to app_writer;
-- No DELETE permission
-- Login role inherits from these
create role app_user login password 'xxx';
grant app_writer to app_user;
-- Revoke default public access
revoke all on schema public from public;
revoke all on all tables in schema public from public;
```
Revoke public defaults:
Reference: https://supabase.com/blog/postgres-roles-and-privileges
---
### 5.2 Enable Row Level Security for Multi-Tenant Data
**Impact: CRITICAL (Database-enforced tenant isolation, prevent data leaks)**
Row Level Security (RLS) enforces data access at the database level, ensuring users only see their own data.
**Incorrect (application-level filtering only):**
```sql
-- Relying only on application to filter
select * from orders where user_id = $current_user_id;
-- Bug or bypass means all data is exposed!
select * from orders; -- Returns ALL orders
```
**Correct (database-enforced RLS):**
```sql
-- Enable RLS on the table
alter table orders enable row level security;
-- Create policy for users to see only their orders
create policy orders_user_policy on orders
for all
using (user_id = current_setting('app.current_user_id')::bigint);
-- Force RLS even for table owners
alter table orders force row level security;
-- Set user context and query
set app.current_user_id = '123';
select * from orders; -- Only returns orders for user 123
create policy orders_user_policy on orders
for all
to authenticated
using (user_id = auth.uid());
```
Policy for authenticated role:
Reference: https://supabase.com/docs/guides/database/postgres/row-level-security
---
### 5.3 Optimize RLS Policies for Performance
**Impact: HIGH (5-10x faster RLS queries with proper patterns)**
Poorly written RLS policies can cause severe performance issues. Use subqueries and indexes strategically.
**Incorrect (function called for every row):**
```sql
create policy orders_policy on orders
using (auth.uid() = user_id); -- auth.uid() called per row!
-- With 1M rows, auth.uid() is called 1M times
```
**Correct (wrap functions in SELECT):**
```sql
create policy orders_policy on orders
using ((select auth.uid()) = user_id); -- Called once, cached
-- 100x+ faster on large tables
-- Create helper function (runs as definer, bypasses RLS)
create or replace function is_team_member(team_id bigint)
returns boolean
language sql
security definer
set search_path = ''
as $$
select exists (
select 1 from public.team_members
where team_id = $1 and user_id = (select auth.uid())
);
$$;
-- Use in policy (indexed lookup, not per-row check)
create policy team_orders_policy on orders
using ((select is_team_member(team_id)));
create index orders_user_id_idx on orders (user_id);
```
Use security definer functions for complex checks:
Always add indexes on columns used in RLS policies:
Reference: https://supabase.com/docs/guides/database/postgres/row-level-security#rls-performance-recommendations
---
## 6. Data Access Patterns
**Impact: MEDIUM**