Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .archon/workflows/e2e-codex-smoke.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
name: e2e-codex-smoke
description: "E2E smoke test for Codex provider. Runs a simple prompt + structured output node."
provider: codex
model: gpt-5.1-codex-mini
model: gpt-5.2

nodes:
- id: simple
Expand Down
2 changes: 1 addition & 1 deletion .archon/workflows/e2e-mixed-providers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ nodes:
- id: codex-node
prompt: "Say 'codex-ok' and nothing else."
provider: codex
model: gpt-5.1-codex-mini
model: gpt-5.2
idle_timeout: 30000

# 3. Assert both providers returned output
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check bundled defaults
run: bun run check:bundled

- name: Type check
run: bun run type-check

Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ workspace/
# Lock files (auto-generated)
package-lock.json

# Auto-generated source (regenerated by scripts/generate-bundled-defaults.ts)
**/*.generated.ts

# Agent commands and documentation (user-managed)
.agents/
.claude/
Expand Down
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ bun run format:check
bun run validate
```

This runs type-check, lint, format check, and tests. All four must pass for CI to succeed.
This runs `check:bundled`, type-check, lint, format check, and tests. All five must pass for CI to succeed.

### ESLint Guidelines

Expand Down Expand Up @@ -710,10 +710,11 @@ async function createSession(conversationId: string, codebaseId: string) {

**Defaults:**
- Bundled in `.archon/commands/defaults/` and `.archon/workflows/defaults/`
- Binary builds: Embedded at compile time (no filesystem access needed)
- Binary builds: Embedded at compile time (no filesystem access needed) via `packages/workflows/src/defaults/bundled-defaults.generated.ts`
- Source builds: Loaded from filesystem at runtime
- Merged with repo-specific commands/workflows (repo overrides defaults by name)
- Opt-out: Set `defaults.loadDefaultCommands: false` or `defaults.loadDefaultWorkflows: false` in `.archon/config.yaml`
- **After adding, removing, or editing a default file, run `bun run generate:bundled`** to refresh the embedded bundle. `bun run validate` (and CI) run `check:bundled` and will fail loudly if the generated file is stale.

**Global workflows** (user-level, applies to every project):
- Path: `~/.archon/.archon/workflows/` (or `$ARCHON_HOME/.archon/workflows/`)
Expand Down
13 changes: 9 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ Thank you for your interest in contributing to Archon!
Before submitting a PR, ensure:

```bash
bun run type-check # TypeScript types
bun run lint # ESLint
bun run format # Prettier
bun run test # All tests (per-package isolation)
bun run check:bundled # Bundled defaults are up to date (see note below)
bun run type-check # TypeScript types
bun run lint # ESLint
bun run format # Prettier
bun run test # All tests (per-package isolation)

# Or run the full validation suite:
bun run validate
```

**Bundled defaults**: If you added, removed, or edited a file under
`.archon/commands/defaults/` or `.archon/workflows/defaults/`, run
`bun run generate:bundled` to refresh the embedded bundle before committing.

**Important:** Use `bun run test` (not `bun test` from the repo root) to avoid mock pollution across packages.

### Commit Messages
Expand Down
3 changes: 2 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default tseslint.config(
'worktrees/**',
'.claude/worktrees/**',
'.claude/skills/**',
'**/*.generated.ts', // Auto-generated source files (content inlined via JSON.stringify)
'**/*.js',
'*.mjs',
'**/*.test.ts',
Expand All @@ -41,7 +42,7 @@ export default tseslint.config(

// Project-specific settings
{
files: ['packages/*/src/**/*.{ts,tsx}'],
files: ['packages/*/src/**/*.{ts,tsx}', 'scripts/**/*.ts'],
languageOptions: {
parserOptions: {
projectService: true,
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
"build": "bun --filter '*' build",
"build:binaries": "bash scripts/build-binaries.sh",
"build:checksums": "bash scripts/checksums.sh",
"generate:bundled": "bun run scripts/generate-bundled-defaults.ts",
"check:bundled": "bun run scripts/generate-bundled-defaults.ts --check",
"test": "bun --filter '*' --parallel test",
"test:watch": "bun --filter @archon/server test:watch",
"type-check": "bun --filter '*' type-check",
"type-check": "bun --filter '*' type-check && bun x tsc --noEmit -p scripts/tsconfig.json",
"lint": "bun x eslint . --cache",
"lint:fix": "bun x eslint . --cache --fix",
"format": "bun x prettier --write .",
Expand All @@ -25,7 +27,7 @@
"build:web": "bun --filter @archon/web build",
"dev:docs": "bun --filter @archon/docs-web dev",
"build:docs": "bun --filter @archon/docs-web build",
"validate": "bun run type-check && bun run lint --max-warnings 0 && bun run format:check && bun run test",
"validate": "bun run check:bundled && bun run type-check && bun run lint --max-warnings 0 && bun run format:check && bun run test",
"prepare": "husky",
"setup-auth": "bun --filter @archon/server setup-auth"
},
Expand Down
78 changes: 78 additions & 0 deletions packages/workflows/src/defaults/bundled-defaults.generated.ts

Large diffs are not rendered by default.

127 changes: 50 additions & 77 deletions packages/workflows/src/defaults/bundled-defaults.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { describe, it, expect } from 'bun:test';
import { readFileSync, readdirSync } from 'fs';
import { join } from 'path';
import { isBinaryBuild, BUNDLED_COMMANDS, BUNDLED_WORKFLOWS } from './bundled-defaults';

// Resolve the on-disk defaults directories relative to this test file so the
// tests work regardless of cwd. From packages/workflows/src/defaults go up
// four levels to the repo root, then into .archon/.
const REPO_ROOT = join(import.meta.dir, '..', '..', '..', '..');
const COMMANDS_DIR = join(REPO_ROOT, '.archon/commands/defaults');
const WORKFLOWS_DIR = join(REPO_ROOT, '.archon/workflows/defaults');

describe('bundled-defaults', () => {
describe('isBinaryBuild', () => {
it('should return false in dev/test mode', () => {
Expand All @@ -12,54 +21,51 @@ describe('bundled-defaults', () => {
});
});

describe('BUNDLED_COMMANDS', () => {
it('should have all expected default commands', () => {
const expectedCommands = [
'archon-assist',
'archon-code-review-agent',
'archon-comment-quality-agent',
'archon-create-pr',
'archon-docs-impact-agent',
'archon-error-handling-agent',
'archon-implement-issue',
'archon-implement-review-fixes',
'archon-implement',
'archon-investigate-issue',
'archon-pr-review-scope',
'archon-ralph-prd',
'archon-resolve-merge-conflicts',
'archon-sync-pr-with-main',
'archon-synthesize-review',
'archon-test-coverage-agent',
'archon-validate-pr-code-review-feature',
'archon-validate-pr-code-review-main',
'archon-validate-pr-e2e-feature',
'archon-validate-pr-e2e-main',
'archon-validate-pr-report',
];
describe('bundle completeness', () => {
// These assertions are the canary for bundle drift: if someone adds a
// default file without regenerating bundled-defaults.generated.ts, the
// bundle would be missing in compiled binaries (see #979 context). The
// generator is `scripts/generate-bundled-defaults.ts`, and
// `bun run check:bundled` verifies the generated file is up to date.

for (const cmd of expectedCommands) {
expect(BUNDLED_COMMANDS).toHaveProperty(cmd);
}
it('BUNDLED_COMMANDS contains every .md file in .archon/commands/defaults/', () => {
const onDisk = readdirSync(COMMANDS_DIR)
.filter(f => f.endsWith('.md'))
.map(f => f.slice(0, -'.md'.length))
.sort();
expect(Object.keys(BUNDLED_COMMANDS).sort()).toEqual(onDisk);
});

expect(Object.keys(BUNDLED_COMMANDS)).toHaveLength(21);
it('BUNDLED_WORKFLOWS contains every .yaml/.yml file in .archon/workflows/defaults/', () => {
const onDisk = readdirSync(WORKFLOWS_DIR)
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
.map(f => f.replace(/\.ya?ml$/, ''))
.sort();
expect(Object.keys(BUNDLED_WORKFLOWS).sort()).toEqual(onDisk);
});

it('should have non-empty content for all commands', () => {
it('bundled content matches on-disk file content (defense against generator corruption)', () => {
for (const [name, content] of Object.entries(BUNDLED_COMMANDS)) {
expect(content).toBeDefined();
expect(typeof content).toBe('string');
expect(content.length).toBeGreaterThan(0);
// Commands should have meaningful content (at least some markdown)
expect(content.length).toBeGreaterThan(50);
const diskContent = readFileSync(join(COMMANDS_DIR, `${name}.md`), 'utf-8');
expect(content).toBe(diskContent);
}
for (const [name, content] of Object.entries(BUNDLED_WORKFLOWS)) {
// Workflows may be .yaml or .yml — prefer .yaml, fall back.
let diskContent: string;
try {
diskContent = readFileSync(join(WORKFLOWS_DIR, `${name}.yaml`), 'utf-8');
} catch {
diskContent = readFileSync(join(WORKFLOWS_DIR, `${name}.yml`), 'utf-8');
}
expect(content).toBe(diskContent);
}
});
});

it('should have markdown content format', () => {
// Commands are markdown files, should have typical markdown patterns
for (const [name, content] of Object.entries(BUNDLED_COMMANDS)) {
// Should contain some text (not just whitespace)
expect(content.trim().length).toBeGreaterThan(0);
describe('BUNDLED_COMMANDS', () => {
it('every command has meaningful content (>50 chars)', () => {
for (const content of Object.values(BUNDLED_COMMANDS)) {
expect(content.length).toBeGreaterThan(50);
}
});

Expand All @@ -76,36 +82,8 @@ describe('bundled-defaults', () => {
});

describe('BUNDLED_WORKFLOWS', () => {
it('should have all expected default workflows', () => {
const expectedWorkflows = [
'archon-assist',
'archon-comprehensive-pr-review',
'archon-create-issue',
'archon-feature-development',
'archon-fix-github-issue',
'archon-resolve-conflicts',
'archon-smart-pr-review',
'archon-validate-pr',
'archon-remotion-generate',
'archon-interactive-prd',
'archon-piv-loop',
'archon-adversarial-dev',
'archon-workflow-builder',
];

for (const wf of expectedWorkflows) {
expect(BUNDLED_WORKFLOWS).toHaveProperty(wf);
}

expect(Object.keys(BUNDLED_WORKFLOWS)).toHaveLength(13);
});

it('should have non-empty content for all workflows', () => {
for (const [name, content] of Object.entries(BUNDLED_WORKFLOWS)) {
expect(content).toBeDefined();
expect(typeof content).toBe('string');
expect(content.length).toBeGreaterThan(0);
// Workflows should have meaningful YAML content
it('every workflow has meaningful content (>50 chars)', () => {
for (const content of Object.values(BUNDLED_WORKFLOWS)) {
expect(content.length).toBeGreaterThan(50);
}
});
Expand All @@ -120,15 +98,10 @@ describe('bundled-defaults', () => {
});

it('should have valid YAML structure', () => {
// Workflows are YAML files, should parse without error
for (const [name, content] of Object.entries(BUNDLED_WORKFLOWS)) {
// Should contain 'name:' as all workflows require a name field
for (const content of Object.values(BUNDLED_WORKFLOWS)) {
expect(content).toContain('name:');
// Should contain 'description:' as all workflows require description
expect(content).toContain('description:');
// Should contain nodes: (with optional loop: inside nodes)
const hasNodes = content.includes('nodes:');
expect(hasNodes).toBe(true);
expect(content.includes('nodes:')).toBe(true);
}
});
});
Expand Down
Loading
Loading