Skip to main content

Development Conventions

Every realm in Yggdrasil is governed by a REGLAS_YGGDRASIL.md file — a living document that defines the rules of that realm. "Reglas" is Spanish for "rules," chosen intentionally to reflect the bilingual context of the BrierStudios team.

REGLAS_YGGDRASIL.md

Each project's REGLAS_YGGDRASIL.md contains:

1. Realm Declaration

# REGLAS_YGGDRASIL — Midgard

**Realm:** Midgard (Personal Apps)
**Proyecto:** brierstudios-site
**Versión:** 1.0.0

2. Naming Conventions

ElementConventionExample
Repositorieskebab-case with realm prefixvana-lilith-discord
Fileskebab-casepersonality-engine.ts
ComponentsPascalCaseNeonButton.tsx
FunctionscamelCaseparseRealmConfig()
ConstantsUPPER_SNAKE_CASEMAX_RETRY_ATTEMPTS
CSS Classeskebab-case with BEMhero__title--glow
Environment VarsUPPER_SNAKE_CASE with realm prefixVANA_DISCORD_TOKEN
Branch Namesrealm/type/descriptionvana/feature/personality-engine

3. Commit Messages

Follow the Conventional Commits specification with a realm prefix:

vana(core): add personality switching pipeline

- Implement personality.yml parser
- Add trait resolution engine
- Write integration tests for personality engine

Refs: VANA-123

Format: <realm>(<scope>): <description>

Common scopes:

  • core — Core functionality
  • ui — Visual changes
  • docs — Documentation
  • test — Test additions/changes
  • ci — CI/CD pipeline
  • perf — Performance

4. Branch Strategy

main (production)
├── develop (staging)
│ ├── <realm>/feature/<description>
│ ├── <realm>/fix/<description>
│ └── <realm>/experiment/<description>
└── hotfix/<description>

Rules:

  • main is always deployable
  • All feature branches branch from develop and merge back via PR
  • muspel/ prefixed branches have relaxed review requirements
  • asgard/ prefixed branches require 2+ approvals

5. Required Files

Every project must include:

├── REGLAS_YGGDRASIL.md # Conventions (this file)
├── .yggdrasil.yml # Project manifest
├── README.md # Project description and setup
├── CHANGELOG.md # Version history
├── LICENSE # License (MIT unless specified)
└── tests/ # Test directory (never empty)

6. Code Review Rules

RealmMin. ReviewersAuto-MergeMerge Strategy
Asgard2ᚻ NeverSquash
Vanaheim1ᛏ If CI passesSquash
Alfheim1ᛏ If CI passesSquash
Svartalfheim1ᛏ If CI passesMerge commit
Muspelheim0ᛏ AlwaysANY
Niflheim1ᛏ If CI passesSquash
HelheimN/AN/ARead-only
Jotunheim2ᚻ NeverSquash
Midgard1ᛏ If CI passesSquash

TypeScript Conventions

Strict Mode

All projects use TypeScript strict mode:

tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true
}
}

Import Style

Use named imports. No wildcard imports.

// ᛏ Good
import { YggdrasilBus } from '@brierstudils/yggdrasil-core';
import type { RealmConfig } from '@brierstudils/yggdrasil-core';

// ᚻ Bad
import * as Core from '@brierstudils/yggdrasil-core';

Error Handling

All errors must be typed. Use Result pattern for expected failures.

// ᛏ Good — typed Result
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };

function deployToRealm(realm: Realm): Result<DeploymentId, DeployError> {
// ...
}

// ᚻ Bad — untyped throw
function deployToRealm(realm: Realm) {
throw new Error('failed'); // what kind of error?
}

Environment Variables

All env vars must be validated at startup using Zod or similar:

import { z } from 'zod';

const envSchema = z.object({
REALM: z.enum(['asgard', 'vanaheim', 'alfheim', 'svartalfheim', 'muspelheim', 'niflheim', 'helheim', 'jotunheim', 'midgard']),
DISCORD_TOKEN: z.string().min(1),
NODE_ENV: z.enum(['development', 'staging', 'production']),
});

const env = envSchema.parse(process.env);

Testing Conventions

Test File Naming

src/
personality-engine.ts
personality-engine.test.ts ← Unit test
personality-engine.integration.test.ts ← Integration test

Coverage Requirements

RealmMinimum CoverageEnforcement
Asgard100%CI blocks on miss
Vanaheim80%Warning at 70%
Alfheim70%Warning at 60%
Midgard70%Warning at 60%
MuspelheimNoneNone
Others80%CI blocks on miss

Test Structure

describe('PersonalityEngine', () => {
// Setup per describe block, not per test
const engine = new PersonalityEngine(LILITH_PROFILE);

describe('resolveTrait()', () => {
it('should return the correct trait value', () => {
expect(engine.resolveTrait('curious')).toBe(true);
});

it('should return undefined for unknown traits', () => {
expect(engine.resolveTrait('nonexistent')).toBeUndefined();
});
});
});

Documentation Conventions

  • Language: English for content, Spanish-neutral terms for conventions (e.g., "Reglas")
  • Format: MDX with frontmatter (title, description, sidebar_position)
  • Admonitions: Use Docusaurus admonitions for callouts (tip, info, warning, caution)
  • Code blocks: Always include language identifier and optional title
  • Links: Prefer relative links over absolute URLs
  • Images: Store in static/img/ with descriptive filenames

Linting & Formatting

The Yggdrasil CLI enforces conventions via yg lint:

# Check all conventions
yg lint

# Auto-fix what's possible
yg lint --fix

# Include realm-specific checks
yg lint --realm-check

# Run on specific realm
yg lint --realm asgard --strict

The linter checks:

  • File naming conventions
  • Commit message format
  • Required files existence
  • Test coverage thresholds
  • Import style (no wildcards)
  • TypeScript strict mode
  • REGLAS_YGGDRASIL.md completeness

Reglas son reglas. Even in the Nine Realms.