-
Notifications
You must be signed in to change notification settings - Fork 0
STRUCTURE
Generated codebase map. The authoritative vocabulary (agent/recipe/service/stack/catalog) is in
ARCHITECTURE.md at the repo root; the data-flow and key abstractions are in
docs/codebase/ARCHITECTURE.md. This document covers where things live, naming conventions,
and where to add new code.
harnessed/
├── src/harnessed/ # Python application — CLI + assembly + launch logic
├── catalog/ # Authored content: agents, recipes, services, stacks
├── tests/ # pytest (unit + podman-gated integration)
├── schemas/ # JSON Schema files for catalog YAML validation
├── docs/ # GitHub wiki clone (git -C docs pull to update)
│ ├── codebase/ # Generated codebase maps (regenerate with /map-codebase)
│ └── guides/ # Recipe/service authoring guides
├── pyproject.toml # Python project: name=harnessed, entry points, deps
├── mise.toml # Runtime version pinning (Python, Node)
├── ARCHITECTURE.md # Vocabulary + build model — read first
├── CONTRIBUTING.md # Dev setup + how to add catalog entries
├── AGENTS.md # Operational notes for AI assistants
└── CLAUDE.md # Project instructions for AI assistants
src/harnessed/
├── __init__.py
├── launcher.py # `harnessed` CLI — Typer app; build/launch/init/test commands
├── cli.py # `harnessed-tools` CLI — argparse; assemble/scan/persist commands
├── assemble.py # Assembly orchestrator — reads catalog, calls emit
├── emit.py # File emitter — writes profile artifacts (EMIT ONLY, no podman)
├── schema.py # Typed dataclasses + catalog YAML loaders + validation
├── paths.py # Single source of truth for all path computation
├── synclinks.py # Skills/commands fan-out with collision detection
├── persist.py # Global persist allowlist + hard-deny guard
├── persist_gc.py # Persist directory lifecycle (list + prune)
├── capability.py # Capability test runner — launches headless, probes instance
├── report.py # Capability test reporter — Rich table or JSON output
└── scan.py # Supply-chain scan runners (osv-scanner, pip-audit, snyk)
Entry points (declared in pyproject.toml):
-
harnessed→harnessed.launcher:main(Typer app) -
harnessed-tools→harnessed.cli:main(argparse)
Import graph (top-down, no cycles):
launcher.py
└─ assemble.py ─── emit.py ─── paths.py
│ └─ schema.py
│
└─ schema.py ────── paths.py
└─ paths.py
└─ persist.py ───── paths.py, schema.py
└─ synclinks.py ─── schema.py
cli.py
└─ assemble.py (same subgraph as above)
└─ capability.py ── schema.py, paths.py
└─ report.py
└─ scan.py
└─ persist_gc.py ── paths.py
catalog/
├── agents/
│ ├── claude/
│ │ ├── agent.yaml # harness, image, dockerfile
│ │ └── (Dockerfile lives at catalog/base/Dockerfile.harnessed-claude)
│ └── omp/
│ └── agent.yaml
├── base/
│ ├── Dockerfile.harnessed-base # shared base: node, mise, uvx, hatago, scanner
│ ├── Dockerfile.harnessed-claude # claude CLI installer (FROM harnessed-base)
│ ├── Dockerfile.harnessed-omp # omp installer (FROM harnessed-base)
│ ├── Dockerfile.harnessed-opencode # opencode installer
│ ├── Dockerfile.harnessed-gemini # gemini CLI installer
│ ├── Dockerfile.harnessed-antigravity
│ ├── Dockerfile.harnessed-codex
│ ├── egress-firewall.sh # mounted into containers at launch
│ └── harnessed-scan # in-image supply-chain scan script
├── recipes/
│ └── <name>/
│ ├── recipe.yaml # required
│ ├── Dockerfile # optional — no FROM, no ARG HARNESS
│ ├── skills/
│ │ └── <skill-name>/ # leaf dir fanned into .claude/skills/
│ ├── commands/
│ │ └── <cmd-name>/ # leaf dir fanned into .claude/commands/
│ └── rules/
│ └── <rule-name>/ # leaf dir fanned into .claude/rules/
├── recipes.local -> ~/.config/harnessed/catalog/recipes (symlink, created by `harnessed init`)
├── services/
│ └── <name>/
│ ├── service.yaml # port, image, run command
│ └── Dockerfile
├── services.local -> ~/.config/harnessed/catalog/services
├── stacks/
│ └── <agent>_<recipe>[_<recipe>…]/
│ └── stack.yaml # harness, recipes[], services[]
├── stacks.local -> ~/.config/harnessed/catalog/stacks
├── agents.local -> ~/.config/harnessed/catalog/agents
└── (agents.local, recipes.local, etc. symlinks created by `harnessed init`)
| Entity | Convention | Example |
|---|---|---|
| Stack |
<agent>_<recipe>[_<recipe>…] — underscores between fields, hyphens within a name |
claude_gstack_ping_time |
| Recipe | single kebab-case name | codebase-memory-mcp |
| Agent | single lowercase name matching the harness |
claude, omp
|
| Service | single kebab-case name | ping-service |
| Skill dir | leaf dir name under skills/
|
time-helper |
| Command dir | leaf dir name under commands/
|
bd-cmd |
| Derived image | harnessed-<stack>:latest |
harnessed-claude-gstack-ping:latest |
| Instance name | harnessed-<stack>-<sha1[:8]> |
harnessed-claude-gstack-ping-ab12cd34 |
schemas/
├── recipe.schema.json # JSON Schema for recipe.yaml
├── stack.schema.json # JSON Schema for stack.yaml
└── agent.schema.json # JSON Schema for agent.yaml
YAML files in the catalog reference these via the yaml-language-server directive:
# yaml-language-server: $schema=../../../schemas/recipe.schema.jsontests/
├── fixtures/ # fixture catalog trees (minimal stacks/recipes for unit tests)
├── __init__.py
├── test_schema.py # recipe/stack YAML parsing and validation
├── test_emit.py # artifact emission (mcp.json, hatago.config, settings.json, Dockerfile)
├── test_paths.py # path resolution (profile dirs, persist dirs, instance names)
├── test_synclinks.py # skill/command fan-out and collision detection
├── test_persist_mounts.py # persist bind-mount construction
├── test_persist_allowlist.py # global persist hard-deny + allowlist guard
├── test_persist_gc.py # persist list/prune lifecycle
├── test_launcher_install.py # launcher image-build helpers
├── test_launcher_init.py # one-time init container dispatch
├── test_launch_secrets.py # varlock secret resolution
├── test_claude_config_seed.py # per-instance .claude.json stub
├── test_omp_auth_seed.py # omp agent mount
├── test_service_refs.py # service-reference URL resolution
├── test_scan.py # supply-chain scan runners
├── test_ensure_docs_wiki_clone.py # docs/ wiki bootstrap
├── test_ensure_local_catalog_links.py # catalog symlink setup
├── test_catalog_json_schemas.py # every catalog YAML validates against its JSON Schema
└── test_recipes_integration.py # LIVE: builds each stack; asserts declared capabilities in container
# Gated: HARNESSED_PODMAN=1 uv run pytest tests/test_recipes_integration.py
Run tests:
uv run pytest -q # fast unit tests — no containers
HARNESSED_PODMAN=1 uv run pytest tests/test_recipes_integration.py # live integrationProfiles are not in the repo — they are written to ~/.local/share/harnessed/profiles/<stack>/.
Overridable via $XDG_DATA_HOME. Structure after a successful build:
profiles/<stack>/
├── .mcp.json # single hatago entry (claude --mcp-config points here)
├── hatago.config.json # full MCP server list for the hatago hub
├── settings.json # merged settings: installer-baked + harnessed's required grants
├── Dockerfile.harnessed-<stack> # generated derived Dockerfile (for inspection/debugging)
├── scan-report.json # supply-chain scan result (surface from the built image)
└── .claude/
├── skills/
│ └── <skill-name>/ # fanned from recipe dirs + extracted from image layers
├── commands/
│ └── <cmd-name>/
├── rules/
│ └── <rule-name>/
├── agents/, hooks/ # extracted from image if baked
└── (settings.json is at profile root, not here)
Per-recipe, per-project dirs for recipe state that must outlive container restarts:
persist/
└── <recipe-name>/
└── <project-hash-8hex>/
└── <entry-name>/ # bind-mounted rw into the container at $HOME/<name>
The project hash is sha1(project_path)[:8] for scope: workspace, or
sha1(git_common_dir)[:8] for scope: project. Managed by persist_gc.py.
~/.config/harnessed/catalog/
├── agents/ # user-private agents (override repo agents by name)
├── recipes/ # user-private recipes
├── services/ # user-private services
└── stacks/ # user-private stacks
The .local symlinks under catalog/ (e.g. catalog/recipes.local) point here, so a repo
checkout can reference user-overlay entries directly when developing locally.
| Task | Location |
|---|---|
| New recipe |
catalog/recipes/<name>/recipe.yaml (+ optional Dockerfile, skills/, commands/, rules/) |
| New agent harness |
catalog/agents/<name>/agent.yaml + catalog/base/Dockerfile.harnessed-<name> + entry in launcher._HARNESS_ATTACH_CMD
|
| New service |
catalog/services/<name>/service.yaml + Dockerfile
|
| New stack (compose recipes) | catalog/stacks/<agent>_<recipe>[…]/stack.yaml |
| New assembler-level validation |
schema.py (parse) + assemble.py (gate) |
| New emitted artifact |
emit.py function + call site in assemble.py
|
| New launcher command |
launcher.py Typer @app.command()
|
| New harnessed-tools subcommand |
cli.py argparse subparser + _run_* handler |
| New path computation |
paths.py (NEVER compute paths in launcher.py directly) |
| New persist logic |
persist.py (security gate) or persist_gc.py (lifecycle) |
| New capability probe |
capability.py + schema.expected_capabilities
|
| New supply-chain scan | scan.py |
| Tests |
tests/test_<module>.py mirroring the module name |
-
EMIT ONLY in
assemble.pyandemit.py— no subprocess, no podman calls. -
paths.pyis the single source for all path computations — never derive profile dirs or instance names inline inlauncher.py. -
No
harnesses:field in recipe YAML — recipes are harness-independent; per-harness behavior belongs inside the recipe's Dockerfile viaARG HARNESS/${HARNESS}. -
Pin every download in recipe Dockerfiles —
validate_pinrejects:latest,@latest,--branch main/masterat assemble time. -
pnpm, not npm/npx —
validate_no_raw_npmenforces this at assemble time. -
Streamable-HTTP MCP only — SSE is deprecated;
type: httpin.mcp.json. -
User overlay wins —
paths.find_in_catalogsearches user catalog before repo catalog. -
Credentials never baked — secrets arrive at launch via varlock env-file or ro credential
mounts; never
--build-arg/ENVin Dockerfiles.
Start Here
Guides
- Recipe authoring
- Service authoring
- Stacks
- Extending stacks (proposed)
- Recipe catalog
- System prompt & rules (proposed)
- Secrets
- AWS SSO
- Pulumi (host login forwarding)
- Egress & exposing services
- Container filesystem
- Git hooks
- Troubleshooting
- Pin management (harnessed update)
Codebase Map
Planning & Roadmap
- open work: GitHub Issues
Research & Prompts
- research/ (home-folder requirements per harness, browse in-repo)
- prompts/ (reusable prompt templates, browse in-repo)