Extending the engine

How to build on HEADING OS: add a skill, a rule, or a script, and clear the gates

How to build on HEADING OS: add a skill, a rule, or a script, and clear the gates before declaring it done. This is the developer how-to. The contribution policy (open an issue first; pull requests are by invitation) lives in CONTRIBUTING.md; read it before sending code.


0. Dev setup

uv sync --all-extras --group dev   # core + all integration extras + dev tools (pytest, ruff, pre-commit)
pre-commit install                 # arm the commit-time gates (once per clone)
uv run python scripts/run-tests.py

A green test run on a fresh clone means your environment is sound.


1. The shape of the engine

Four kinds of artifact, each with its own home and conventions:

Artifact Lives in Is
Skill .claude/skills/{name}/SKILL.md a slash-command workflow, routed from natural language
Rule .claude/rules/*.md always-on or path-scoped behavior the agent follows
Script scripts/*.py a CLI tool or daemon; shared code in scripts/utils/
Hook wired in .claude/settings*.json a PreToolUse / PostToolUse / SessionStart guard

Before building anything: search for an existing pattern and reuse it. The standards below are summarized from the engine’s own development rules.

Editing the documentation site itself (docs/) has its own contract: every page is either Markdown-sourced (regenerated) or hand-authored HTML, and a drift guard fails the build if the two fall out of sync. See DOCS-PIPELINE.md before editing anything under docs/.


2. Writing a skill

A skill is a folder with a SKILL.md. The frontmatter is a contract:

---
name: example-skill                    # kebab-case
description: >                          # what it does, when to use, AND when NOT to
  One paragraph. Name the alternative skill for the cases this one should not handle.
argument-hint: "[target]"
allowed-tools: "Read, Bash(python3:*)"  # least privilege
metadata:
  author: Your Name
  email: you@example.com
  version: "1.0"
x-heading-orchestration:                    # how the orchestrator may dispatch it
  parallel_safe: false                  # true | partial | false
  shared_state: []                      # paths it writes to
  triggers: ["example phrase"]          # natural-language triggers, or []
---

Rules of the road:

The /skill-creator skill scaffolds and evaluates a new skill against these standards.


3. Writing a rule

Rules in .claude/rules/ load automatically. A rule with no frontmatter is always active; a rule with a paths: list loads only when work touches those paths:

---
paths:
  - "scripts/**"
---

Keep rules concise and single-purpose. Several existing rules encode security controls (the send-gate, the engine/data separation, the secret guards); adapt brand and voice rules freely, but leave the security ones in place.


4. Writing a script

#!/usr/bin/env python3
"""One-line purpose. Usage examples in the docstring."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from scripts.utils.workspace import get_workspace_root   # reuse, don't reinvent
# ... argparse CLI ...
if __name__ == "__main__":
    main()

5. The gates before “done”

Run these on anything you add or change:

uv run python scripts/sanitize-text.py <file> --scan   # zero hidden characters
uv run python -m py_compile <script>                    # Python syntax
uv run python scripts/run-tests.py                      # the suite

ruff (the linter) and the secret scan run automatically through pre-commit. Then:

The slice standard

Non-trivial work runs on Canopus, the build standard: seven numbered steps, two of them the operator’s own approval moments, and three instruments that measure whether the approval meant anything. Canopus, the build standard is the full page: the steps, the four check clauses, what probe reads, and the two places the standard reports rather than blocks.

One criterion is worth carrying here rather than following a link for, because it is the one a builder acts on: probe runs the contract twice against null-stubbed modules carrying different values, and a test that never FAILS under either run is vacuous. Passing, skipping and erroring all leave a test unproved; only a failure shows it read the value.

The three commands you will actually type while contributing:

python scripts/canopus.py probe tests/contract/<date>-<slug>/   # before approval
python scripts/canopus.py check --range origin/main..HEAD       # after the build
python scripts/canopus.py note <slug> ...                       # when it ships

Two facts worth carrying here rather than looking up. The approval is a COMMIT, not a lock file: git show <sha>:<path> reads the frozen bytes, git diff answers whether the contract moved, and git merge-base --is-ancestor answers whether the implementation descends from the approval. Nothing on this machine holds those bytes down, and the CI clause that reads them REPORTS a break rather than blocking one. Do not describe either as prevention.

The steps themselves are defined once, as data, in scripts/utils/canopus_steps.py. The /canopus skill and the page above both summarise that module; neither may renumber it.


6. Testing discipline

The suite lives in tests/ (security tests in tests/security/). Every behavior you change needs a test that exercises the real pattern through the public interface, not an implementation detail. Write one test, make it pass, then the next.

When debugging, build a fast reproduction first: a failing test or a deterministic harness that makes the bug appear and disappear on demand. Do not hypothesize about a cause you cannot reproduce. Write the regression test before the fix, watch it fail, apply the fix, watch it pass.


7. Restraint


8. Reference

File Role
CONTRIBUTING.md Contribution policy (issues, PR by invitation)
scripts/run-tests.py The test runner
tests/, tests/security/ The regression suite
.claude/skills/skill-creator/ Scaffolds and evaluates a new skill
scripts/utils/ Shared modules to reuse
pyproject.toml Pinned dependencies, ruff / pytest config

HEADING OS · Extending the engine · maintained by Misha Hanin · see also Architecture for how the pieces compose and Security model for the controls your code inherits.