Skip to content

feat(hooks/atomic): cross-repo EF pillar-classification enforcement (Cure 4b — companion to dojo-os PR #2381) #31

@lapc506

Description

@lapc506

Resumen (ES)

Hoy (2026-05-28) en dojo-os aterrizamos PR #2381 con un patrón de 4 curas de defensa-en-profundidad contra una deriva real: agentes (y humanos) clasifican Edge Functions por la dependencia externa que tocan (Stripe → ¿Launchpad?) en vez de por su propósito (per-Path SKU → Pathways). El caso fundador fue stripe-create-path-checkout-session: una EF de Pathways (origen DOJ-4002) que un agente etiquetó como "Launchpad/Stripe EF" porque vio Stripe en el código.

Las 4 curas en dojo-os:

  1. Cura 1 — Ownership explícito: docs/repo-health/edge-function-classification.md lista cada EF con su pillar canónico y ticket de origen.
  2. Cura 2 — Clasificación activa: .claude/scripts/classify-ef-pillar.sh clasifica por header ((DOJ-NNNN)), no por dependencia.
  3. Cura 3 — Regla de agente: sección "Pillar Classification" en CLAUDE.md + memoria por-sesión feedback_pillar_by_purpose_not_dependency.md.
  4. Cura 4a — Hook a nivel repo: .claude/hooks/pre-write-ef-pillar-header.sh (PreToolUse) bloquea EFs nuevas sin (DOJ-NNNN) en las primeras líneas.

Lo que falta — Cura 4b: defensa cross-repo. Otros repos que ya alojan o eventualmente alojarán Edge Functions o código atribuido por pillar (dojo-academy, internal-team-trainings, civic-tech, etc.) no tienen el guardrail. Si cualquier repo futuro crea código pillar-attributed sin la convención, la misma deriva recurre.

English summary

The drift: classify-by-dependency vs classify-by-purpose. dojo-os PR #2381 (today) shipped Cures 1–4a as a repo-local defense. Cure 4b lifts the hook into make-no-mistakes-toolkit so any repo using the toolkit can opt in via config, generalizing the rule beyond DojoCodingLabs (org-agnostic ticket-prefix knob).


Agent Layer

Scope

Add a toolkit-level PreToolUse hook (natural home: hooks/atomic/, parallel to the existing pre-atomic.sh / post-atomic-drift.sh pattern from the DOJ-4064 atomic-drift-thesis work).

Detection

  • Trigger: Write tool calls to a path matching the configured EF folder pattern (default: supabase/functions/*/index.ts).
  • Rule: first 25 lines of the file content must contain a Linear ticket reference matching the configured prefix regex (default: [A-Z]{2,4}-[0-9]+).
  • Generalization: do not hardcode DOJ-. The toolkit is org-agnostic; encode the prefix as a config knob so other Linear workspaces (and other ticket systems with similar shapes) can adopt.

Enforcement output

  • Exit 1 with structured stderr guidance:
    • Why this matters (drift thesis link)
    • How to fix (add (TICKET-NNNN) to the EF header, pointing to the origin ticket — not the dependency)
    • Pointer to the repo's local edge-function-classification.md (if present)
  • The hook MUST be a no-op when the rule is disabled in config (default: disabled to avoid breaking existing toolkit consumers).

Configurability

Per-repo opt-in via a new config file hooks/atomic/edge-function-pillar-rules.json (shape parallel to the existing .atomic-design-rules.json consumed by atomic-design-toolkit):

{
  "enabled": false,
  "edge_function_paths": ["supabase/functions/*/index.ts"],
  "ticket_prefix_regex": "[A-Z]{2,4}-[0-9]+",
  "header_scan_lines": 25,
  "classification_doc_path": "docs/repo-health/edge-function-classification.md"
}

Repos adopt by:

  1. Copying the config file into their repo root (or .claude/).
  2. Setting enabled: true.
  3. Tuning edge_function_paths (some repos use AWS Lambda / Cloudflare Workers layouts).
  4. Tuning ticket_prefix_regex for their workspace.

Optional companion: bootstrap slash command

/make-no-mistakes:ef-pillar-init — parallel to the existing v1.14 atomic-rules-init pattern. Scaffolds:

  • hooks/atomic/edge-function-pillar-rules.json
  • A starter docs/repo-health/edge-function-classification.md template
  • Settings entry to register the hook

Acceptance Criteria

  • Hook implemented at hooks/atomic/pre-write-ef-pillar-header.sh (matches dojo-os semantics + adds ticket-prefix config knob)
  • Config schema at hooks/atomic/edge-function-pillar-rules.json (shape parallel to .atomic-design-rules.json)
  • Optional slash command /make-no-mistakes:ef-pillar-init to bootstrap adoption in a new repo
  • Documentation at references/edge-function-pillar-classification.md (parallel to references/atomic-design-hooks-setup.md from the v1.2.0 atomic-design-toolkit precedent)
  • Unit tests in hooks/atomic/__tests__/ covering: (a) EF with valid ticket header → pass, (b) EF missing ticket → block + exit 1, (c) non-EF path → no-op, (d) rule disabled in config → no-op
  • CHANGELOG entry + README mention
  • Backward-compatible: default enabled: false. Existing toolkit consumers (dojo-os, atomic-design-toolkit, etc.) opt in explicitly. No silent breakage.

Tradeoffs (honest)

  • Cost: ~150–250 LOC hook + config + docs, plus ~50–80 LOC tests.
  • Benefit: prevents dependency-vs-purpose drift across all repos using the toolkit. Same defense, one source of truth.
  • Risk — generalization: Edge Function is not a universal concept. AWS Lambda layouts, Cloudflare Workers, Vercel functions, etc. differ. Mitigation: the edge_function_paths config knob lets each repo declare its own EF layout. The rule's essence (a ticket-of-origin in the header of pillar-attributed code) is layout-agnostic.
  • Risk — false positives: a legitimate refactor that moves code between EFs without changing ownership might be blocked. Mitigation: the rule only fires on Write to NEW files (existing files are not re-scanned). Edits to pre-existing EFs without ticket headers are out of scope (handled by a separate audit, not this hook).
  • Maintenance: regex pattern + ticket-prefix knob need clear docs. The reference doc must explain how to configure for non-DojoCodingLabs adopters.

Context Files


Created by Claude Code on behalf of @lapc506

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions