sea-generator-first
Enforces the generator-first workflow for SEA-Forge development. Activates when working in the SEA codebase and any intent to modify code, add features, fix bugs, or implement changes. Redirects code-editing intent to spec-first workflow and enforces that all generated code zones are never hand-written. Triggers for any attempts to edit libs or apps directories, generated code, feature implementation, bug fixes, or adding new entities, ports, or adapters.
SKILL.md
| Name | sea-generator-first |
| Description | Enforces the generator-first workflow for SEA-Forge development. Activates when working in the SEA codebase and any intent to modify code, add features, fix bugs, or implement changes. Redirects code-editing intent to spec-first workflow and enforces that all generated code zones are never hand-written. Triggers for any attempts to edit libs or apps directories, generated code, feature implementation, bug fixes, or adding new entities, ports, or adapters. |
name: sea-generator-first description: Use when implementing or fixing behavior in SEA-Forge, especially when generated outputs may change. Enforces spec-first regeneration and requires semantic fixture updates whenever libs//src/gen/ changes.
SEA-Forge Generator-First Guardrail
IRON RULE: Specifications are the source of truth. Code is generated from specs, never written by hand.
No-Technical-Debt Rule
This skill forbids placeholders of any kind. Do not introduce or suggest:
- TODO/FIXME/XXX/HACK/STUB markers
- Stubs, mocks-as-implementations, or empty handlers
raise NotImplementedError/throw new Error("not implemented")pass,..., or any placeholder logic
Exceptions (per technical-debt.instructions.md):
pass/...in@abstractmethodfunctions is allowedpass/...in.pyistub files is allowed- Abstract classes inheriting
ABCorProtocolare allowed
If an issue blocks progress, request explicit user feedback with options and a recommendation. Never silently stub.
The Generator-First Contract
┌──────────────────────────────────────────────────────────────┐
│ GENERATOR-FIRST CONTRACT │
├──────────────────────────────────────────────────────────────┤
│ 1. Files in **/src/gen/** are NEVER hand-written │
│ 2. All structural changes start in .sea or .sds.yaml │
│ 3. Pipeline: spec → AST → IR → manifest → code │
│ 4. Regeneration produces identical output (determinism) │
│ 5. After pipeline, run gap-report; run last-mile only if gaps exist │
│ 6. After code changes, always update semantic fixtures │
│ 7. Full CI (`just ci`) must pass — not just `just ci-quick` │
└──────────────────────────────────────────────────────────────┘
Forbidden Zones (Never Edit)
| Path Pattern | Why | Fix Instead |
|---|---|---|
**/src/gen/** | Generated from manifest | Edit spec, run just pipeline <ctx> |
docs/specs/**/*.ast*.json | AST projection | Edit .sea file |
docs/specs/**/*.ir.json | IR projection | Edit .sea or .sds.yaml |
docs/specs/**/*.manifest.json | Compiled manifest | Edit spec, re-run pipeline |
docs/specs/**/fixtures/semantic/*.semantic.fixture.yaml | Generated fixture baseline | Add/update a separate *.fixture.yaml scenario file in the same folder |
Files with @generated / Generated by / DO NOT EDIT in first 10 lines | Generated output | Fix upstream spec or generator |
| Any file derived from a manifest | Generated output | Fix upstream spec or generator |
tsconfig.base.json path mappings | Generator-owned | Update tools/nx/ templates and regenerate |
Detecting Generated Code
Check the first 10 lines for banners:
- Python:
# @generated,# Generated by,# DO NOT EDIT - JS/TS:
// @generated,// Generated by,// DO NOT EDIT
Partial generation uses begin/end markers:
- Python:
# BEGIN GENERATED/# END GENERATED - JS/TS:
// BEGIN GENERATED/// END GENERATED
Safe Edit Zones
| Path Pattern | What Goes Here |
|---|---|
docs/specs/**/*.sea | SEA-DSL domain specifications |
docs/specs/**/*.sds.yaml | SDS YAML specifications |
docs/specs/**/*.md | ADR, PRD documentation |
docs/specs/**/fixtures/semantic/*.fixture.yaml | Hand-authored semantic intent fixtures required by CI guard |
tools/codegen/ | Python codegen pipeline (gen.py, templates, validators) |
tools/codegen/templates/*.jinja2 | Jinja2 code generation templates |
generators/ | Nx generators and TypeScript templates |
libs/<ctx>/adapters/src/ (non-gen) | Handwritten integration adapters (must implement SDS port interfaces) |
libs/<ctx>/**/tests/ | Unit and integration test files |
tests/ | Top-level test files |
Integration Adapter Exception: Files in libs/<ctx>/adapters/src/ (outside src/gen/) are handwritten. They must implement the port interfaces defined by the SDS. This includes files like libs/documentation/adapters/src/handlebars-template.adapter.ts.
Mandatory Pre-Push Guard Parity
Always run the same guard used by CI before pushing changes that touch generated outputs.
GITHUB_BASE_REF=<base-branch> scripts/ci/spec_and_fixtures_guard.sh
Use your PR target for <base-branch> (dev, main, etc.).
If the guard reports:
Generated code changed, but no upstream specs changedGenerated code changed, but no semantic fixtures changed
then you must add/update at least one file under:
docs/specs/**anddocs/specs/<context>/fixtures/semantic/**(ordocs/specs/shared/fixtures/semantic/**)
before pushing.
Intent Interception Workflow
Trigger Detection
Intercept when agent detects ANY of these intents:
- "Add feature X" / "Fix bug in Y" / "Create entity Z"
- "Implement port P" / "Add adapter A"
- "Modify code in libs/" / "Edit code in apps/"
- "Remove NotImplementedError" / "Implement handler"
Decision Tree
Request arrives
│
├─ Does it target **/src/gen/** or a generated file?
│ ├─ YES → ⚠️ Stop and ask (see below)
│ └─ NO → Continue normally
│
├─ Does it target tools/codegen/ or generators/?
│ └─ YES → Edit allowed (generator is the correct upstream fix)
│ └─ After fixing, regenerate affected contexts
│
├─ Does it target libs/<ctx>/adapters/src/ (non-gen)?
│ └─ YES → Edit allowed (handwritten adapter zone)
│
└─ Does it target docs/specs/**?
└─ YES → Edit allowed (specs are source of truth)
└─ After editing, validate and run pipeline
Stop and Ask if a Generated Zone Is Targeted
⚠️ **Implementation Gap Detected**
This request requires editing generated code or outputs, but:
- Generated outputs must be changed via specs and regeneration
- Handwritten edits in generated zones are forbidden
- [Reference gap from `docs/workdocs/code_archeology.md` if applicable]
**Options:**
1. **Spec-first path**: Update specs, run pipeline, regenerate (recommended)
2. **Generator fix**: Fix the codegen template or gen.py if the defect is in the pipeline
3. **Clarify requirements**: Provide missing spec details first
Which approach? I recommend option 1 unless the defect is in the generator itself.
Spec-First Workflow
Step 1: Check What Exists
ls docs/specs/<context>/*.sea docs/specs/<context>/*.sds.yaml 2>/dev/null
Step 2a: If Spec Exists — Edit, Validate, Generate
-
Edit the spec file (
.seaor.sds.yaml) -
Validate:
just sds-validate docs/specs/<context>/<context>.sds.yaml just sea-validate docs/specs/<context>/<context>.sea -
Run full pipeline:
just pipeline <context>This single command validates specs, parses SEA-DSL, builds AST/IR/manifest, and generates code. Do NOT run
just codegenseparately —just pipelinealready invokes it. -
Check for missing adapters:
just gap-report <context> -
If gaps exist, generate adapters:
just last-mile <context> -
Run semantic validation:
just semantic-check <context> -
Verify determinism:
just regen-check docs/specs/<context>/<context>.manifest.json -
Run full CI:
just ci
Step 2b: If Spec Does Not Exist
- Use
spec-authoringskill to create ADR → PRD → SDS → SEA-DSL - Ensure proper CQRS annotations:
@cqrs,@tx,@outbox - Then follow Step 2a
Step 3: Verify No Debt Markers
rg -n "NotImplementedError|TODO|FIXME|HACK|STUB|_execute_logic" \
libs/<context>/**/src/gen/ 2>/dev/null
Expected: zero matches. If debt markers appear in generated output, the defect is in the Jinja2 template, not the spec.
Quick Reference: Just Recipes
| Recipe | Purpose |
|---|---|
just pipeline <ctx> | Full spec → code pipeline (validate + parse + AST + IR + manifest + codegen) |
just sds-validate <file> | Validate SDS YAML against schema |
just sea-validate <file> | Validate SEA-DSL syntax |
just sds-compile <file> | Compile SDS YAML to manifest.json |
just codegen <manifest> | Generate code from manifest (prefer just pipeline instead) |
just gap-report <ctx> | Show missing adapters for a context |
just last-mile <ctx> | Generate all missing adapters and tests |
just last-mile-all | Run last-mile for all contexts |
just semantic-check <ctx> | Governance validation (runs semantic-fixtures-ctx first) |
just semantic-fixtures | Validate all semantic fixture files |
just regen-check <manifest> | Verify regeneration produces identical output |
just spec-guard | Validate all specs with cross-check |
just flow-lint | Flow annotation compliance check |
just ci | Full local CI (lint + spec + test + determinism) |
just ci-quick | Fast check (lint + specs only — insufficient for PRs) |
Common Patterns
Adding a New Entity
- Edit domain
.seaor.sds.yamlto add entity definition - Run
just pipeline <context> - Entity appears in
libs/<context>/domain/src/gen/ - Run
just gap-report <context>to check for cascading gaps
Adding a New Command or Query
- Add command/query definition to
.sds.yamlundercqrs.commandsorcqrs.queries - Specify
inputfields,outputtype (for queries), andtouches.aggregates(for commands) - Run
just pipeline <context> - Handler appears in
libs/<context>/application/src/gen/commands/orqueries/ - Update semantic fixtures with scenarios for the new handler
Adding a New Port
- Edit
.sds.yamlports section - Run
just pipeline <context> - Port interface appears in
libs/<context>/ports/src/gen/ - Run
just gap-report <context>to see missing adapter implementations - Run
just last-mile <context>to generate adapter templates
Fixing a Bug in Generated Code
Three possible root causes — diagnose before fixing:
-
Stale generated code (spec changed but pipeline wasn't re-run):
- Run
just pipeline <context>to regenerate
- Run
-
Spec defect (spec defines wrong structure):
- Fix the
.sds.yamlor.seafile - Run
just pipeline <context>
- Fix the
-
Generator defect (template or gen.py produces wrong output):
- Fix the Jinja2 template in
tools/codegen/templates/ortools/codegen/gen.py - Add regression test in
tools/tests/test_codegen_templates.py - Regenerate all affected contexts:
just pipeline <context> - Use
debugging-codegen-pipelineskill for systematic triage
- Fix the Jinja2 template in
NEVER edit src/gen/ files. Trace backward: generated code → manifest → IR → AST → spec.
Known Generator Pitfalls
These are real defects that have occurred in the codegen pipeline. Watch for them:
| Pitfall | Symptom | Root Cause | Fix Location |
|---|---|---|---|
| Dict-style query output not unwrapped | NoneReadRepositoryPort, List["None"] in generated query | gen.py receives {"type": "Foo"} but expects string | tools/codegen/gen.py |
Command prefix not in _build_entity guard | _execute_logic referenced but never defined | command.py.jinja2 prefix whitelist incomplete | tools/codegen/templates/command.py.jinja2 |
'result_entity' in dir() runtime guard | Masks variable assignment bugs at runtime | Old template artifact | tools/codegen/templates/command.py.jinja2 |
| Filesystem import path in generated code | from libs.ctx.domain.src.gen.types import ... | Template uses filesystem path instead of package path | tools/codegen/templates/command.py.jinja2 |
Bare except Exception | Swallows KeyboardInterrupt, SystemExit | Template catches too broadly | Template handlers |
sea CLI not found (exit code 127) | just sea-parse or just sea-validate fails | No console_scripts entry in sea_dsl package | Use the Manual Pipeline Fallback (Graph.parse_to_ast_json()) or install sea_dsl with proper console_scripts so CLI wrappers resolve |
expected EOI / Parse error in SEA parse | SEA-DSL file uses # comments | PEG parser only supports // comments | Replace all # comments with // |
SDS ports format error ('str' has no attribute 'get') | ir_to_manifest.py crashes on ports section | SDS ports: is a dict instead of a list of dicts | Use list format: ports:\n - name: PortName\n id: PORT-001 |
Manual Pipeline Fallback
When just pipeline <ctx> fails due to environment issues (broken venv, missing CLI), run each step individually:
# Step 1: Parse SEA → AST (uses Python API directly)
.venv/bin/python -c "
from sea_dsl.sea_dsl import Graph
with open('docs/specs/<ctx>/<ctx>.sea') as f:
source = f.read()
ast_json = Graph.parse_to_ast_json(source)
with open('docs/specs/<ctx>/<ctx>.ast.json', 'w') as f:
f.write(ast_json)
"
# Step 2: AST → IR
.venv/bin/python tools/ast_to_ir.py docs/specs/<ctx>/<ctx>.ast.json docs/specs/<ctx>/<ctx>.ir.json
# Step 3: IR → Manifest
.venv/bin/python tools/ir_to_manifest.py docs/specs/<ctx>/<ctx>.ir.json docs/specs/<ctx>/<ctx>.manifest.json
# Step 4: Manifest → Code
.venv/bin/python tools/codegen/gen.py docs/specs/<ctx>/<ctx>.manifest.json
Note: For the
sharedcontext, the files are namedkernel.sea,kernel.sds.yaml, etc.
Creating a New Bounded Context
- Use Nx generator:
nx g @workspace/scaffolding:bounded-context - Create
.sds.yamlindocs/specs/<context>/ - Run
just pipeline <context> - Run
just gap-report <context>andjust last-mile <context>
CI Sync Workflow
"Generated outputs are out of sync"
just pipeline <context>
git add docs/specs/<context>/*.ast.json docs/specs/<context>/*.ir.json \
docs/specs/<context>/*.manifest.json libs/<context>/**/src/gen/
If libs/<context>/**/src/gen/ changed, also add/update a non-generated semantic fixture file:
docs/specs/<context>/fixtures/semantic/*.fixture.yaml
"Generated code changed, but no semantic fixtures changed"
Add or update a non-generated fixture file, for example:
docs/specs/<context>/fixtures/semantic/<context>-gen-surface.fixture.yaml
Do not hand-edit <context>.semantic.fixture.yaml because it is generator-managed.
- Add new scenarios with required fields:
- id: "ISSUE-CONTEXT-HANDLER-001"
title: "Handler does something specific"
classification: happy_path # or: edge_case, error_case, feature, security
requirement_ref: "SDS-XXX:CMD-YYY-001"
cycle_ref: "374"
given: "Precondition state"
when: "Action performed"
then: "Expected outcome"
- Add to
expected_invariantsif the scenario defines system invariants - Add to
traceabilitysection linking to issue/PRD/SDS/ADR - Validate context first:
just semantic-fixtures-ctx <context> - Optionally validate all fixtures:
just semantic-fixtures
"Generated code changed, but no upstream specs changed"
This is blocked by scripts/ci/spec_and_fixtures_guard.sh.
- If generated diffs were accidental drift, revert generated outputs.
- If generated diffs are intentional, add/update upstream spec evidence under
docs/specs/**. - Add/update semantic fixtures under
docs/specs/<context>/fixtures/semantic/**. - Re-run:
GITHUB_BASE_REF=<base-branch> scripts/ci/spec_and_fixtures_guard.sh
"Debt markers found in generated code"
Per technical-debt.instructions.md, debt markers are absolutely prohibited. Fix the generator template so it produces complete code (no stubs). If blocked, ask the user for guidance — do not introduce placeholders.
Commit Checklist
Before pushing, verify:
- No hand-edits to
**/src/gen/**or other generated zones -
just pipeline <context>ran after spec changes -
just gap-report <context>shows no unresolved gaps (orjust last-milewas run) -
just regen-check <manifest>passes (determinism) -
just semantic-check <context>passes (governance) - If
libs/**/src/gen/**changed, add/updatedocs/specs/**/fixtures/semantic/*.fixture.yamlin the affected context -
GITHUB_BASE_REF=<base-branch> scripts/ci/spec_and_fixtures_guard.shpasses locally -
just cipasses (full CI, notci-quick) -
git status --porcelainshows only intended changes
Related Skills
- spec-authoring — Creating ADR/PRD/SDS documents
- sea-dsl-authoring — Writing SEA-DSL specifications
- sea-last-mile — Adapter generation after codegen
- sea-generator-builder — Creating new generators
- debugging-codegen-pipeline — Systematic codegen triage
- sds-yaml — SDS YAML authoring with CQRS mappings
- governance-validation — CALM compliance and spec-guard