HI CLI Scaffolding Runbook
HI CLI Scaffolding Runbook
Use this runbook when a repository adopts or refreshes the Harness Intelligence CLI. From CLI 6.0.0, the CLI installs one Baseline from the public Registry. The Registry Baseline spec, its architecture, and the glossary define the terms used here.
Registry Baseline model
Registry. A static, public set of Registry Items in the shadcn registry format, served at https://api.harness-intelligence.devpunks.com/r (a rewrite on the API domain to the Vercel Blob store harness-intelligence-registry). Override the URL with HI_REGISTRY_URL or the registry field in .devpunks/settings.json. The committed settings registry must be an https: URL; file:// and loopback http: URLs (localhost, *.localhost, 127.0.0.1, [::1]) work only through HI_REGISTRY_URL, for local builds and tests. A disallowed URL is never contacted: every Registry fetch fails as RegistryUnavailable naming the rule. When a command uses a non-default Registry, its human output names it: hi init, hi update, hi diff, and hi check start with Registry: <url> (not the default public Registry)., and hi tools ensure adds a non-default-registry warning. JSON output never carries the notice, so hi check --json stays exactly the status object the session hook parses. Registry Item names (catalog entries, items, and registryDependencies) must be lowercase kebab-case and not registry; the publisher and the installer enforce the same rule. The CLI sends no credential. The Registry is the only Baseline source; the npm package ships only the executable dist/index.js (with the control-plane configuration bundled) and no Baseline, so hi init and hi update need network access.
| Path | Content | Cache |
|---|---|---|
registry.json | Latest catalog with latest, version, cliVersionRange, and one entry per Registry Item with meta.sha256 | 60 s; overwritten on each publish |
<version>/registry.json | Catalog of one Baseline | Immutable |
<version>/<item>.json | One Registry Item | Immutable |
Baseline. One immutable Registry version named YYYY.MM.DD-<short sha>. A correction is a new Baseline. Publishing moves latest; there is no promotion step. Each Baseline declares a compatible CLI range; the CLI 6 range is >=6.0.0 <7.0.0. A CLI outside the range refuses the Baseline, writes nothing, and names the range; run hi upgrade.
Download cache. The installer caches items and versioned catalogs under ${XDG_CACHE_HOME:-~/.cache}/hi/registry/<version>/. The catalog meta.sha256 verifies a cached item; a mismatch is discarded and fetched again. A path that would leave the cache directory is never read, written, or deleted; it is simply not cached. The sha256 is never used for drift.
Local state. Two files, one writer each. No file in the repository stores a content hash of a Managed Artifact.
| File | Writer | Content |
|---|---|---|
.devpunks/settings.json (Project settings) | A human, hi init, or the agent on request. hi update never writes it (one exception: the one-time migration adds packs). | Intent: packs, lint.scopes, lint.exclude, providers, requiredTools, commitGate, optional registry, optional typescript.compilerProjects (see Codex TypeScript semantic reads). |
.devpunks/installed.json (Installed Record) | Only the installer, last in each run. | baseline, shape (Recorded Shape: workspaces with languages and technologies, excluding the wiki root; Pack ids; wiki root; docsRoot when a docs directory exists and git does not ignore it. Installer-written files such as oxlint.config.ts, oxlint.local.ts, and anything under dot-directories or opensrc never count as TypeScript evidence, so an install cannot cause shape-drift), items, paths (path to item and artifact kind), dependencies (devDependencies the installer added per workspace; a required package the project already had is never recorded, so it is never removed), failedLinks. |
Packs. pack-core is always installed: shared prompt, prompt specs, handoff, subagents, harness links, hooks, managed lint runner, Commit Gate, and tools. Other Packs come only from settings packs. Detection proposes Packs at hi init; a detected Pack that is not in settings is information, never drift.
Managed Artifact kinds.
| Kind | Rule | Examples |
|---|---|---|
| Copied Artifact | Written verbatim from a Registry Item. hi update overwrites it; identical content is skipped. | Skills, hook scripts, runner scripts, anti-slop lint files, .agents/AGENTS.md |
| Built Artifact | Rendered from a Registry Item template, Project settings, and the Recorded Shape. hi update re-renders it; identical content is skipped. | .devpunks/specs/prompts/**, .devpunks/AGENT-HANDOFF.md, .devpunks/AGENT-SYSTEM-PROMPT.md, .devpunks/specs/lint/selection.json, <scope>/oxlint.config.ts, .devpunks/commit-gate-contracts.json, harness agent files |
| Authored Artifact | Written once when absent; never compared or overwritten. | Root and scoped AGENTS.md starters, .agents/subagents/manifest.mjs, .codex/config.toml, <scope>/oxlint.local.ts, Project Skills |
Structured merges add Harness keys to repository-owned JSON and YAML files (.claude/settings.json, .cursor/hooks.json, lefthook.yml, workspace package.json scripts) without replacing other keys.
Project Skills. A skill under .agents/skills/<id> that no Registry Item provides is a Project Skill. The installer never removes, compares, or overwrites it. A real skill directory under .claude/skills, .codex/skills, .cursor/skills, or .opencode/skills moves to .agents/skills/<id> before the harness links are created. When .agents/skills/<id> already exists, a byte-identical harness copy is removed and a differing one moves to .agents/skills/[DEPRECATED] <id> (<harness>), so the harness directory can become the link without losing either copy. When a later Baseline provides a skill with the same id, the Baseline skill wins: the Project Skill directory is renamed .agents/skills/[DEPRECATED] <id> and the report lists the rename.
Harness Adapters. The CLI builds each subagent body once from .agents/subagents/manifest.mjs (Authored and self-contained, since the CLI loads it from a data: URL; the Built .agents/subagents/manifest.prompt.md guides tailoring it per workspace) and wraps it in one envelope per harness: .claude/agents, .cursor/agents, and .opencode/agents get Markdown with YAML front matter; .codex/agents gets TOML with developer_instructions and [[skills.config]]. All four harnesses are always built. Only hi update rebuilds these files; after editing the manifest, hi diff reports them stale until hi update runs. The harness item links .claude/skills to ../.agents/skills and .claude/CLAUDE.md, .codex/AGENTS.md, and .opencode/AGENTS.md to ../.agents/AGENTS.md.
Current Commands
hi init
hi init --yes
hi init --json
hi update
hi update --yes
hi update --json
hi diff
hi diff --json
hi check
hi check --json
hi tools ensure
hi commit-gate verify
hi report --help
hi operator status
hi operator install
hi operator update
hi operator migrate
hi upgrade --help
hi --version
hi -vhi scaffold, hi ensure, hi update --check, and hi update --write are retired in CLI 6.0.0. hi add does not exist.
hi init
hi init [--yes] [--json] runs once per repository, and again to reconfigure settings:
- Fetch the catalog and check the CLI range. When the Registry is unreachable (
unavailable) or the range refuses the CLI (refused), it writes nothing. - Detect the repository once and propose Packs and Software Scopes. Proposed Software Scopes are the TypeScript workspaces, excluding the wiki root and a monorepo root.
- Propose providers: the repository manager comes from the git remote, and the backlog provider defaults to the repository manager when that is a valid backlog provider. A re-run pre-fills every value from the existing settings.
- Confirm Packs, Software Scopes, repository manager, backlog provider, asset provider, Commit Gate policy, and the Product/Backlog Root URL. The confirmation is interactive only on a TTY without
--yes; otherwise the proposal is accepted as is, including the proposed Software Scopes. Reviewlint.scopesafter a non-interactive run. - Run the
hi updatepipeline in init mode with the confirmed settings. The pipeline writes.devpunks/settings.jsononly after merge-target validation, so a refused, unavailable, or invalid-target run writes nothing.
Exit code: 0 only when the Baseline is applied; 1 for partial, refused, or unavailable.
{
"packs": ["docs", "planning", "quality", "react", "typescript"],
"backlogProvider": "linear",
"backlogProjectUrl": "https://linear.app/acme/initiative/product-backlog-root-1234567890ab",
"repositoryManager": "github",
"assetProviderSlug": "linear",
"requiredTools": ["agent-browser", "gh", "opensrc", "portless", "skills"],
"commitGate": "enabled",
"lint": { "scopes": ["apps/web"], "exclude": [] }
}Backlog choices are GitHub Projects/Issues, Linear, Azure DevOps, and monday.com. backlogProjectUrl identifies the provider Product/Backlog Root; it is trimmed and must be absolute HTTP(S). For Linear, a https://linear.app/<workspace>/project/<project> Epic Project URL is rejected; run hi init again and enter https://linear.app/<workspace>/initiative/<root-initiative>. Scaffolded write-backlog reads backlogProvider and backlogProjectUrl before provider work and stops when the destination is missing or cannot be proven to be the configured root. Legacy wiki provider files are hints only.
The Registry installs no wiki: wiki setup belongs to the owning project, and hi never creates or aligns a wiki. This amends Registry Baseline SPEC OUT-004 and OUT-006 by user decision on 2026-09-28. See Wiki.
After init, activate $hi-cli and follow its Init branch:
- Read
.devpunks/installed.json, then.devpunks/AGENT-HANDOFF.md, then.devpunks/AGENT-SYSTEM-PROMPT.md. - Activate
writing-for-agents, thenrule-authoring. Author root and scopedAGENTS.mdfiles from the prompt specs in.devpunks/specs/prompts/**, withCLAUDE.mdsymlink mirrors. - Tailor
.agents/subagents/manifest.mjsto the real owned paths, guidance files, and skills, following the Built guide.agents/subagents/manifest.prompt.md. Keep the manifest self-contained, with no relative imports: the CLI loads it from adata:URL. Then runhi updateso the Harness Adapters rebuild the harness agent files. - Report moved Project Skills and every
[DEPRECATED] <id>rename. - Review the saved
lint.scopesandlint.excludeagainst the real software owners (see managed lint). - Run the existing wiki structure check, then
$docs-onboardingagainst a passing wiki root. - Read matching
opensrc/*.mdcards before relying on third-party package internals. - Verify worker handoff for the active harness: its settings enable subagents,
.agents/subagents/manifest.mjsexists, and the selected specialist owns the target paths with matching guidance files and skills. Repair a failed guard or report the blocker; do not implement in the main thread.
hi update
hi update [--yes] [--json] installs the latest Baseline and never prompts. The pipeline runs in a fixed order, and every step before the Installed Record is idempotent:
- Fetch
registry.jsonand check the CLI range. Refusal writes nothing. - Plan the migration of a manifest-based repository in memory (see migration).
- Resolve the Packs in settings to Registry Items through
registryDependencies. - Plan every path: kind, action, merges, links, dependencies, stale paths.
- Validate that every JSON and YAML merge target parses, is not a symlink, and has no parent directory resolving outside the repository (
InvalidMergeTarget). One invalid target stops the run before any write. Only then write Project settings (hi init's selection or the migration's rewrite) and apply the migration's skill moves and deletions. - Write Copied Artifacts; skip identical content.
- Render Built Artifacts with Project settings and the Recorded Shape; skip identical content.
- Write absent Authored Artifacts; rename a colliding Project Skill to
[DEPRECATED] <id>. - Apply structured merges. A rewritten file keeps its file mode.
- Create symlinks. A file the previous Installed Record lists as Copied or Built is replaced by the link a later Baseline declares there; without
--yes, a locally edited Copied file is kept, reportedlink-failed, and stays recorded. Any other existing path is project-owned and blocks the link. A failure is reported with its path and target and recorded infailedLinks. No copy is made; the next update retries. - Add required workspace devDependencies. Remove a devDependency the installer added and no installed lint asset requires, only when no first-party source in that workspace imports it; otherwise keep and report it. When devDependencies changed, or a recorded devDependency is not installed (an earlier install failed), run the package-manager install once with
--ignore-scripts(dependencyInstallisran; a failed install makes the runpartial). Then run each installed Registry Item'spostInstallcommands in every workspace (root included) whosenode_modules/.binholds the command's executable (isolated installs such as bun keep tools per workspace), else at the root, but only those in the CLI allowlistallowedPostInstallCommands(today onlyeffect-tsgo patch --no-typescript --oxlint); any other command is skipped and named in thedependencyInstalldetail. Commands run without a Unix shell: the executable is spawned directly (throughcmd.exeon Windows, wherenode_modules/.binholds.cmdshims), so allowlist entries hold no shell syntax. They get a minimal environment:PATHwith that workspace's and the rootnode_modules/.binfirst,HOME,TMPDIR,SystemRoot, andPATHEXT. A failing command makes the runpartial. - Remove stale Copied and Built files and symlinks; report stale Authored Artifacts. Without
--yes, a stale Copied file with a local edit is kept, reportedstale-reported, and stays in the Installed Record. - Write
.devpunks/installed.jsonlast. - Sync TypeScript semantic reads. With the TypeScript Pack selected, ensure the pinned Typegraph Tool Runtime under
${XDG_DATA_HOME:-~/.local/share}/hi-tools/typegraph-mcp/<version>, discover and load-probe the Compiler Projects, and write one Codex server per passing project into HI's fenced# BEGIN hi typegraphblock in.codex/config.toml. Without the Pack, remove only that block; the runtime is never pruned. An unavailable runtime (Node older than 22.18, nonpm, failed install) is reported with its reason and writes no entry; the status staysapplied. With the Pack, the Built.devpunks/AGENT-HANDOFF.mdalso gets a TypeScript semantic reads section. See Codex TypeScript semantic reads. - Run
node .agents/scripts/managed-lint-runner.mjson the real repository when Software Scopes exist.lintispassed,findings,failed, orskipped; findings do not change the update exit code. - Report one row per path.
Without --yes, a locally edited Copied Artifact is kept (kept). If the Baseline also changed that file, the run is partial and tells you to run hi update --yes. With --yes, the local edit is overwritten and reported (overwritten-local-edit); git keeps the local version. Authored Artifacts are never overwritten, with or without --yes. When the Commit Gate added lefthook, the report prints a lefthook install hint. A run interrupted at any step converges when run again. There is no temporary repository copy, lint preview, or pending-publication marker.
Exit code: 0 only when status is applied; 1 for partial or refused and when the Registry is unavailable. Lint findings never change the exit code.
The report has mode (init or update), status (applied, partial, refused), the previous and applied Baseline, rows (path, item, artifact, action, detail), lint, dependencyInstall, migration, failedLinks, requiredTools, semanticReads (runtime, projects, excluded, codexConfig, conflicts) unless the run refused, and refusal when the run refused. Row actions: written, skipped, overwritten-local-edit, created, kept, merged, linked, link-failed, dependency-added, dependency-removed, dependency-kept, removed, stale-reported, moved-project-skill, renamed-project-skill, migrated-deleted, migrated-skipped.
On Windows, symlink creation can fail with EPERM without Developer Mode or administrator rights. Enable Developer Mode, then run hi update again.
Migration from the manifest-based flow (historical)
The first hi update in a repository that has .devpunks/scaffold-manifest.json and no Installed Record migrates in the same run. The migration is planned in memory and applied only after merge-target validation. It reads the old settings for Packs, Software Scopes, providers, and required tools; rewrites settings with the old selected Packs as packs and without the old CLI-managed version fields (the one settings write outside hi init); runs detection once to build the Recorded Shape; moves skills from .devpunks/pre-existing-skills into .agents/skills as Project Skills; keeps every Authored Artifact; and deletes the retired files with one migrated-deleted row each. A move or deletion whose path resolves outside the repository through a symlinked directory (for example .agents/scripts or .agents/skills) is never followed: it is left in place and reported as a migrated-skipped row, and the archive stays while it still holds an unmoved skill. A Copied file whose raw bytes no longer match the sha256 the old manifest recorded is a local edit: it is kept unless --yes. The digests are read once from the manifest and never stored.
.devpunks/harness-projection-receipt.json,.devpunks/context-plan.json,.devpunks/specs/lint/assets.json.devpunks/replaced-scaffold/,.devpunks/replaced-skills/, and the emptied.devpunks/pre-existing-skills/.devpunks/commit-gate-lifecycle-receipt.json,.devpunks/required-tools.json,.devpunks/specs/subagents/manifest-spec.json,.devpunks-cache/.agents/scripts/sync-subagents.mjsand.agents/scripts/harness-projection/
scaffold-manifest.json is deleted last, just before the Installed Record, so an interrupted migration starts again on the next run. No separate migrate command exists.
hi diff
hi diff [--json] runs the Drift Check. For each managed path it compares local bytes, the Registry Item at the installed Baseline, and the Registry Item at the latest Baseline, and writes nothing. Built Artifacts compare by rendering at the installed Baseline with the Recorded Shape; a different render with the current shape is shape-drift. Copied Artifacts compare after normalizing line endings and trailing whitespace only. Authored Artifacts are never byte-compared; a changed template is information. Offline, hi diff works from the download cache at the installed Baseline; hi init and hi update cache the applied Baseline's versioned catalog, so this holds right after the first install.
| Class | Meaning |
|---|---|
update-available | Local matches the installed Baseline; the latest Baseline differs. |
local-edit | Local differs from the installed Baseline; upstream is unchanged. |
conflict | A Copied Artifact has a local edit and an upstream change. |
shape-drift | A Built Artifact renders differently with the current repository shape, for example after adding a workspace. |
missing | A recorded path is absent. |
stale | A Built output no longer matches its input, for example harness agent files after a manifest.mjs edit. |
link-failed | A recorded symlink is missing or could not be created. |
Exit code: 1 when any row is not current; information rows (such as a changed Authored template or a detected Pack that is not selected) never change it.
hi check and session hooks
hi check [--json] fetches only the root registry.json, compares the installed Baseline with latest, checks the CLI range, and checks the tools in settings requiredTools. Without the TypeScript Pack, it reads only installed.json and settings and fetches no Registry Item. The JSON result has status, installed, latest, cliVersionRange, cliCompatible, missingTools, detail, and semanticReads.
semanticReads is present only when the Installed Record lists typegraph-runtime (TypeScript Pack). Only then does hi check also read the installed Baseline's catalog and typegraph-runtime item (download cache first), HI's block in .codex/config.toml, and one load probe per Compiler Project. It reports three separate facts: runtime (installed, version, path, reason), registered (count, servers), and compilerProjects[] (tsconfig, server, probe: pass, fail or ambiguous, registered, reason). Human output prints Typegraph runtime: installed <version> (<path>) or Typegraph runtime: not installed: <reason>, Codex entries: <n> registered, and Compiler projects: <n> pass, <n> fail[, <n> ambiguous], then one line per project that did not pass. These facts never change status. Probes are bounded to 10 s each and 30 s together, so the session-start hook stays under its 45 s timeout; past the budget, projects report not probed: hi check probe budget (30 s) spent. See check health.
status | Meaning |
|---|---|
current | The installed Baseline is latest. |
update-available | A newer Baseline exists; run hi update. |
unavailable | The Registry could not be reached. The output names the cached installed Baseline and never states that drift is absent. |
not-installed | .devpunks/installed.json does not exist; run hi init. |
Exit code: 0 for all four statuses; 1 only when a local state file cannot be read.
The session-start hook (.agents/hooks/scaffold-update-check.mjs) runs hi check --json and keys only on status:
| Result | Session message |
|---|---|
No installed.json | Harness Baseline not installed in this worktree; run hi update (or hi init). |
current | Harness Baseline <installed> is current. |
update-available | Harness Baseline update available: <installed> -> <latest>. Run hi update. |
unavailable | Harness Registry unavailable. Installed Baseline: <installed>. Drift was not checked. |
No status | hi check did not return a status. Drift was not checked. |
Both hooks are Copied into .agents/hooks/ and linked from .claude/hooks, .codex/hooks, and .cursor/hooks. Registration merges into .claude/settings.json and .cursor/hooks.json, and the Authored .codex/config.toml enables them for Codex. OpenCode gets both hooks as plugins .opencode/plugins/scaffold-update-check.js and .opencode/plugins/format-edited-file.js. When installed.json is absent it prints not-installed guidance.
The edit hook (.agents/hooks/format-edited-file.mjs) reads protected paths (Copied and Built) from the Installed Record and does not format or lint them. When the record is missing, nothing is managed and the hook never blocks an edit. It does not rebuild harness agent files.
Managed lint scopes and policy
The issue 224 contract
separates saved software ownership, project policy, and generated execution.
Use $hi-cli and its references/managed-lint.md for the operating-agent flow.
Select owners before lint adoption
Inventory shallow apps/* and packages/* packages plus declared workspaces.
Root applications and custom roots such as app/backend/core are supported.
Inspect manifests, source, and command/install context to classify real software
and embedded projects. Candidate discovery is evidence; saved exact selection
alone authorizes managed JavaScript/TypeScript lint. A broad workspace glob does
not enroll everything it matches.
Merge explicit selection into .devpunks/settings.json, preserving its other
fields. hi init provides the managed lint ownership choice (run it again to
change it); authorized settings authoring uses the same contract:
{
"lint": {
"scopes": ["apps/api", "apps/web", "packages/core"],
"exclude": ["apps/api/fixtures/embedded/**", "examples/demo/**"]
}
}lint.scopes contains normalized exact repository-relative package roots, not
globs. Each owner needs a contained package.json and supported JS/TS
command/install context. Reject absolute or escaping paths, missing manifest
roots, and canonical aliases that escape or duplicate ownership. Ordering and
repeated paths normalize deterministically. lint.exclude contains
repository-relative file/directory globs and defaults to [] when omitted.
Missing lint or lint.scopes means selection is required. Explicit
"scopes": [] deliberately disables managed software lint. hi init --yes
saves detection's proposed TypeScript workspaces; review them as candidates. Select "." only for deliberately owned root software;
a root with tooling dependencies normally coordinates other owners. Rootless
repositories select supported nested packages without creating a root manifest.
wiki, app/wiki, and apps/wiki, including descendants, are excluded even as
workspaces; selecting them is invalid. Save additional documentation and embedded
example/fixture exclusions explicitly. Unselected nested package/project
boundaries do not become source of an ancestor. Ordinary test, tests, .test,
and .spec source remains covered. Names merely containing test or wiki are
not universal exclusions. Each eligible file has one most-specific selected
owner; . excludes nested boundaries and more-specific owners. Exclusions win
over selection and rule overrides.
New source inside an owner is covered automatically. A new package is a candidate
for deliberate selection; a moved or missing owner is drift. Update saved scopes
and exclusions, then run hi update. Keep inferred frameworks and generated routes out of settings.
.devpunks/specs/lint/selection.json is a Built Artifact that carries managed lint
selection and execution routes.
One effective config and route
Each owner uses <scope>/oxlint.config.ts as its effective execution target.
It composes supported Harness presets and locally justified framework assets,
then explicit Project Lint Policy. Together with exclusions and failure semantics,
this is the Effective Lint Policy. Existing compatible authored JSON/JSONC stays
at its original project-owned path as an explicit input, including shared parent
policy and renamed oxlint.base.json. oxlint.project.json is the convention for
new policy, not a mandatory rename or a generated copy of authored policy.
Project lint rules (oxlint.local.ts)
The Built <scope>/oxlint.config.ts is rendered only from the selected lint
assets, settings, and the Recorded Shape; it never reads the file it replaces.
It imports the scope's Authored oxlint.local.ts and applies it last, so
project rules win over Ultracite, preset, and asset rules. Put project rules,
categories, env, overrides, plugins, jsPlugins, and ignorePatterns
there; hi update never changes it. Without a project config it starts as a
policy-free default. On adoption or migration it is seeded once from the scope's
existing oxlint.config.ts; when that file is already the Built one (an
interrupted adoption), from the last committed version (git show HEAD:). The
seed strips TypeScript syntax to evaluate the config, keeps only what the managed
config does not already provide, and keeps the project's own extends entries.
A config that cannot be evaluated is preserved verbatim under a comment. Every seeded rule names a built-in Oxlint plugin or a jsPlugins alias
the local file declares; rules for undeclared aliases, and off rules for JS
plugins the managed config never loads, are dropped because Oxlint rejects an
unknown plugin prefix even for an off rule. The Built config uses the same
filter. The import keeps its .ts extension, which Node requires; a tsconfig
that type-checks the config files needs allowImportingTsExtensions.
The root scope (.) never lints the Managed Artifact homes .agents/,
.claude/, .codex/, .cursor/, .devpunks/, .devpunks-cache/,
.opencode/, and opensrc/: its Built config ignores them and the router
excludes them from enumeration and Commit Gate file lists. Lint asset
devDependencies are pinned exact versions; the Registry build fails for an
asset devDependency without a pinned version.
Inventory direct configs, script config arguments, and transitive extends.
Preserve rules/options, categories, overrides, ignores, environments, globals,
plugin/settings references, and typed settings with their path-relative meaning.
Preview added defaults and semantic differences. Dynamic or incompatible policy
stays intact with a named migration conflict when safe composition is unproven.
During adoption, direct read-only oxfmt --check leaves under a check
alias remain format checks; mutating formatter commands still conflict.
When a shared ancestor policy has an ignore or override glob with a literal
path prefix provably outside a selected owner, that pattern is omitted from
that owner's effective config. Overlapping ambiguous globs still conflict.
Opaque root orchestration remains a named conflict until its lint route is
verified without dropping its other checks.
This repository's root lint uses
node ".agents/scripts/managed-lint-runner.mjs" to dispatch to its selected
owners when an operator requests full managed lint. The saved lint.exclude
mirrors the CLI Oxlint ignores for apps/cli/skills/**, apps/cli/dist/**, and
apps/cli/.devpunks-cache/**, keeping generated assets out of the process
argument list. check:repo runs bun run lint:repo && bun run format:repo:
lint:repo checks only root-owned scripts, while format:repo retains the
existing root Oxfmt check. Existing findings elsewhere in a selected scope do
not enter the required root static gate. The root check command preserves the
Turbo wrapper, workspace checks, and cache-policy tests:
node scripts/behavior-contract/run-turbo.mjs check '//#check:repo' '//#test:cache-policy'Framework applicability requires evidence from that owner: manifest, source, framework/test config, or actual command use of shared tooling. Hoisting, root installation, a package name, or broad guidance packs alone are insufficient. React email may justify React without Next/TanStack; Jest does not imply Vitest.
One derived Lint Route supplies cwd, explicit config, supported project-local
tool/version, exclusions, and threshold to package scripts, root dispatch,
CI using those scripts, Lefthook, edited-file verification, and update validation.
Managed execution disables nested config discovery and has no global/latest
binary fallback. Root dispatch adds no second broad . pass. Compare equivalent
verification on identical bytes, file sets, toolchains, and modes; edited-file
formatting/fixes precede verification and may change bytes.
Severity and failure threshold are distinct. New generated lint keeps
--max-warnings 0; represented custom thresholds must agree across routes.
Unrepresentable custom thresholds remain migration conflicts. Precommit formatting
is read-only; edited-file mutation keeps its existing file safety and retry limits.
Exclusions apply before lint/format coverage and empty-work detection. Nonempty
explicit file requests proven to contain only excluded paths return successful
empty work without invoking lint even when unrelated lint selection is missing
or stale. Empty file lists and requests without paths retain owner/policy health
checks. Configuration and declared dependency authority edits still require
validation; malformed available authority and an explicitly invalid scope fail
closed. Mixed
changes check only eligible work once per owner/check kind. Renames affect both
endpoints; deleted paths are never passed as files.
Shared policy/settings/toolchain changes select dependent owners without creating
an implicit root scope. Existing Python tooling retains its routing and honors
shared exclusions.
Adoption, health, and next action
Rootless lint-route health and prompt-authoring completion are separate obligations. A valid selected nested owner can coexist with bounded prompt-authoring findings; report both without inventing a root package or claiming the whole setup is healthy.
hi diff previews the lint changes a new Baseline or a settings change would make.
Inventory known lint/check aliases, repository checks, and CI routes. Preserve
verified compatible commands; preserve and name opaque or incompatible
command/config conflicts until deliberately reconciled.
hi update applies the lint Registry Items: anti-slop files Copied into each
TypeScript Software Scope with their per-asset devDependencies, the runner scripts
Copied, .devpunks/specs/lint/selection.json, .devpunks/specs/lint/README.md,
and <scope>/oxlint.config.ts Built, <scope>/oxlint.local.ts Authored once,
and the per-scope package.json scripts.lint merged. It then runs managed lint on the real repository and
reports lint: passed, findings, failed, or skipped. Operational, config,
or tool failures (failed) block dependent activation. Source findings authorize
no bulk fix and do not change the update exit code. Preserve disabled Commit Gate
policy and consumer hook ownership.
hi update removes only stale Copied lint files that the Installed Record lists;
git keeps their previous bytes. Modified project policy and files the installer
did not record survive; exclusion alone never authorizes deletion.
Large Software Scopes (issue #232). The runner streams Oxlint output through
a temporary file, so a report of any size (including a 13.5 MB JSON report) is
classified as findings, not as an operational failure. It batches file arguments
below the operating-system limit and accepts --files-from <path> (one
repository-relative path per line) for large file sets.
| Result | Operator action |
|---|---|
| Selection required | Classify candidates and save exact scopes/exclusions before dependent activation. |
| Invalid scope or stale route | Correct the named path/settings issue and run hi update to re-render the Built lint output. |
| Intentional exclusion or no eligible work | Report the excluded path or empty work; this is not whole-repository cleanliness. |
Explicit lint.scopes: [] | Managed JavaScript/TypeScript ownership is disabled; existing Python/Ruff routing remains independent. |
hi diff reports stale or shape-drift on lint output | Run hi update; do not hand-edit Built lint files. |
| Project needs a different rule, category, or ignore | Edit the scope's oxlint.local.ts; declare any JS plugin its rules name. |
| Lint findings | Retain diagnostics and threshold; repair only authorized targets. Findings remain distinct from operational failure. |
| Config/tool/process failure | Retain owner, cwd, config, tool/version, command, and original diagnostics; repair that boundary before activation. |
| Command/policy/ownership conflict | Preserve the named file/command and resolve its competing authority within existing authorization. |
Finish with affected validation and one hi check --json. A current status
does not by itself establish correct live lint policy. Retain unresolved
conflicts instead of claiming convergence.
The retained dp-ai single-file witness at
b326ba1a350db5feb071db3ef822228b807bfc5c demonstrated policy divergence under
Oxlint 1.80.0. It is historical defect evidence, not full consumer Mac/CI or final
managed-entrypoint parity proof.
Issue 181 lint feedback
Issue 181 introduced the bounded feedback protocol below. The managed lint contract supersedes its nearest-config execution and recursive wiki lint assumptions; file safety and provider feedback remain required.
Historically, issue 203 made generated wiki/plugin namespaces load together during recursive root lint and aligned planning with materialized JSON policy. That proof remains historical. Issue 224 selects explicit software owners, excludes wiki from managed lint/format, and preserves project policy as inputs to one effective config per owner.
All selected assets share the workspace's plugin namespace mapping, including package-targeted transition rules.
Managed JavaScript/TypeScript workspaces use the locked oxlint@1.80.0, ultracite@7.10.7, and oxfmt@0.66.0 toolchain. Effect workspaces additionally use @effect/tsgo@0.39.0 and oxlint-tsgolint@7.0.2001; the root owns the single effect-tsgo patch --no-typescript --oxlint prepare segment. Each selected JavaScript/TypeScript owner runs managed lint from its own directory through the saved Lint Route, with an explicit effective Oxlint config and nested config discovery disabled. Formatting retains its own owner-local configuration.
The managed edited-file hook formats only the accepted file, applies safe Oxlint fixes only to that file, then performs a read-only JSON verification. Findings are returned to the active provider through its native hook response: Codex blocks PostToolUse with actionable continuation context, Claude receives current-turn context, Cursor receives a user message, and OpenCode records one structured log. Clean verification is silent; an unchanged diagnostic fingerprint is repairable at most three times.
Affected-file CI recognizes saved managed lint scripts and checks changed files with
each owner's effective Oxlint config. Root static verification checks root-owned
source and configuration while preserving the rest of the repository check graph.
Run root bun run lint to inspect all 11 selected scopes.
Fresh worktrees
Fresh worktrees need their own installed dependencies. Run the repository's declared package-manager install with its frozen lockfile, including required prepare scripts, during worktree setup. A tracked Installed Record does not install Oxfmt, Oxlint, or their plugins. Codex edit hooks run after matching Codex tools; they do not watch external editor saves or replace normal validation gates.
For deliberate operator checks, use bounded targets:
bunx ultracite check apps/example/src/file.ts
bunx ultracite doctor
bunx ultracite fix apps/example/src/file.ts
bunx ultracite fix --codex apps/example/src/file.tsultracite fix --codex starts a separate Codex CLI process. It is never invoked by the realtime hook, which stays under Harness file-only safety rules. Ultracite AI rules, skills, prompts, and generated project-wide hooks are not copied into the default Harness context packs.
Commit Gate lifecycle
At runtime, the runner derives the active Git worktree root with git rev-parse --show-toplevel and resolves every repository-relative owner path beneath it, including in a linked worktree. A legacy contract root is accepted for compatibility but never selects the execution directory. Fresh materialized contracts contain repository-relative owner path values and omit root. If an owner directory is missing and a quality command cannot launch, the failure names the owner and command kind and reports the resolved cwd plus the underlying launch code and message (such as ENOENT), alongside any stdout and stderr.
For eligible JavaScript package-manager consumers, Commit Gate is enabled by
default. hi init writes the operator's commitGate choice (enabled or
disabled) to settings; an absent key means enabled. The commit-gate
Registry Item installs the runner (.agents/scripts/commit-gate-runner.mjs,
Copied), .devpunks/commit-gate-contracts.json (Built from settings and the
Recorded Shape), one HI-owned pre-commit.commands.lint entry through a YAML
merge into lefthook.yml, and the root lefthook devDependency. Gate
placement is not Software Scope selection. Opting out never pins a user-owned
Lefthook installation. Format-check validation rejects wrapped mutating commands
that use the short -w flag.
The merge is idempotent and preserves consumer-owned Lefthook commands. It
does not use an unrelated hook's commands block. Disabling an existing gate
returns an ownership-aware handoff: uninstall Lefthook when HI is the sole
owner, or remove only HI's lint entry when the manager is shared. Contracts declare command execution independently for lint and format checks:
| Mode | Selection and invocation |
|---|---|
files | Select affected owner files; substitute the explicit {files} placeholder with existing staged paths relative to that owner. Deleted paths are never arguments. |
owner | Run the owner's aggregate command unchanged when that owner is affected. |
repository | Run the declared aggregate unchanged for staged repository changes; suppress subordinate checks of the same kind. |
Root changes, deletions, and rename source/destination paths participate in coverage. An owner whose only staged changes are deletions is covered without running its file checks. A lint configuration change still expands to the whole owner, and a rename out of a scope still needs coverage: without an applicable aggregate it produces an actionable coverage error. Nested owners remain independently selectable. Invocations with the same final command, working directory, and staged-file environment execute once; arbitrary script names or similar command text do not establish equivalent coverage. Legacy contracts without execution metadata remain readable with their historical filename forwarding; hi update re-renders the contracts with explicit modes.
The gate never passes file sets through argv or environment strings. It lists staged files with an explicit 256 MiB buffer and hands each file list to the managed lint runner through a temporary file named by HI_STAGED_FILES_PATH (or stdin), so a large staged set or an owner file set beyond ARG_MAX does not fail. A lint configuration change still lints the full owner file set, and the gate exits with the lint result.
hi update renders one contract per Software Scope in files mode: lint runs the managed lint runner with --files-from {files-from} (the staged list file), and the format check runs the scope's oxfmt --check {files} in argument batches. commitGate: "disabled" is honored by the gate itself. Settings commitGateChecks.repository.lint and .formatCheck add one root contract in repository mode: the command runs once at the root for every staged path outside the wiki, lint.exclude, and Managed Artifact homes, including repository-level paths outside all Software Scopes (scripts/, .github/, root config), and replaces the per-scope check of the same kind. The wiki is always excluded from the Commit Gate.
Managed lint scripts consume the selected owner’s explicit Lint Route. New generated lint retains --max-warnings 0; diagnostic warnings and failure thresholds are separate. Preserve represented custom thresholds across managed routes, and retain incompatible commands as named migration conflicts. The format check remains read-only. Successful command output retains its original streams; failed-command evidence includes owner, command, exit, and diagnostics. A custom command’s successful exit alone does not establish managed-route parity.
The consumer's required CI lint and read-only format checks remain merge authority. Configure those jobs to run the declared repository commands against the full checkout and propagate nonzero exits; custom lint scripts must explicitly enforce the intended warning policy. A local staged-file gate or a findings-bearing hi update lint run does not establish repository-wide cleanliness. This CLI change does not rewrite consumer workflows or prove that their current jobs fail on warnings. In this repository, bun run test:ci includes workspace lint/check and the root check:repo gate; evaluate those actual scripts and workflow selection when investigating a green CI result with known findings.
After hi update prints its lefthook install hint, run lefthook install, then hi commit-gate verify.
That command only observes: it checks the live dependency, lockfile, configuration, active
Git-resolved hook, and exact HI pre-commit entry, and writes no file. CI remains
merge authority; LEFTHOOK=0 and git commit --no-verify are still possible
bypasses.
Agent Rule
Activate $hi-cli before acting on hi output or generated .devpunks artifacts.
delivery-phase is a human-only entrypoint: its skill frontmatter sets
disable-model-invocation: true and its Codex policy sets
allow_implicit_invocation: false. Invoke it explicitly when the delivery
brief is accepted.
The skill is responsible for reading post-command handoffs, following the Built prompt specs, authoring repository-owned guidance for the real repo shape, and reporting unresolved Harness friction.
Reusable Harness skills are authored and committed only on the checked-out main branch of /home/stefan/repos/skills. Before editing, verify git -C /home/stefan/repos/skills branch --show-current returns exactly main, and stop otherwise. Alternate branches and worktrees are out of scope for authoring commits. Push that source and publish an immutable source tag first, then run bun run sync:skills from this repository so apps/cli/skills/* is generated from that bounded public source tree. Verify the receipt SHA, then publish a Registry Baseline only when requested. The repository skill-sync source pin is refs/tags/sync/registry-baseline-fd3e7ea83053 at fd3e7ea830538d0277df93a5f4f790e488c6dd05; reproduce issue #205's immutable snapshot with HI_SKILLS_REPOSITORY_REF=refs/tags/sync/issue-205-effect-backend-structure-4e2496c5982c bun run sync:skills (commit 4e2496c5982caf6e53c526d5ecc0acf031e4853a). Explicit refs are sync/read-only selections and do not change the main-only authoring rule. The receipt records the requested ref and exact commit.
This immutable pin governs repository skill synchronization. Operator commands resolve canonical shared-skills main once per command and use that captured commit for every affected scope and readback.
Skill activation follows the authoritative frontmatter contract: React Doctor activates automatically only for an explicit Doctor request or production changes overlapping React runtime APIs. Changed-scope scans resolve the checked-out branch's configured upstream and pass it with --base; when no upstream exists, stop for user-selected base authority. TDD activates for explicit TDD, test-first, RED/GREEN, or unresolved behavior-design intent. Verify Behavior is not available through top-level automatic discovery: Debugging Phase invokes reproduce for reported failures, and Implement Spec invokes verify for acceptance criteria.
python-backend-structure is the canonical Python backend domain and import-structure overlay selected by the Python pack and Python subagents. Those selections place the agnostic backend-domain-structure base immediately before the Python overlay. python-project-structure is a deprecated compatibility ID whose shim only redirects to the canonical skill. The source-first integration is pinned to wearedevpunks/skills@506dce0f287f5e672de87c17d7e8c0921e9058c6.
Backend Composition Ownership
backend-domain-structure assigns every composition boundary recursively to its nearest honest parent. A leaf or module assembles only capabilities it owns. The nearest common parent composes public child capabilities; sibling children remain independent, while parent actions or services own cross-child product policy. The process production root closes the remaining graph and selects purely technical bindings between independent public contracts. Framework overlays own the concrete folders and dependency-injection mechanics for this portable rule.
effect-backend-structure owns the Effect module boundary: public services/<capability>/service.ts capabilities, private operations/<use-case>/operation.ts orchestration, private services/<capability>/binding.ts seams beside their public service, models, narrow repositories, representation-changing mappers, and colocated tests. Module internals stay private; relative imports stay inside a module and source aliases cross module boundaries. It also maps Layer ownership to a domain layer.ts, a parent-business Layer for public child Layers, and a process root such as platform/effect/app.ts for cross-module adapters and production requirements. Layer.merge and Layer.mergeAll combine sibling outputs while accumulating requirements; Layer.provide consumes provider output, while Layer.provideMerge retains it downstream. $effect-service-design owns the separate service-qualification, authority-seam, application-policy, Layer, and reusable-test-substitute decisions.
frontend-domain-structure now requires sibling scans and a domain-worthiness classifier before creating feature or module boundaries. It applies the same semantic ownership and public, one-way, acyclic dependency rules recursively, while allowing only a justified one-way peer-feature edge through a public entrypoint. React scopes split at cohesive responsibility and behavior-test seams, keep tiny parent-owned JSX local, and validate at the owning component, hook, model, or feature seam. The source-first integration is pinned to wearedevpunks/skills@211ce3b6bf93319732f261da2f66b419c0262a52.
hi update installs consumer .agents/skills/* as Copied Artifacts from the Registry. For a targeted active-skill refresh in this Harness checkout, first prove the exact local source contains only the intended skill, then install that one copied skill:
npx skills add ./apps/cli/skills/agnostic/requirements/write-backlog --list
npx skills add ./apps/cli/skills/agnostic/requirements/write-backlog \
--skill write-backlog --agent codex --copy --yesInspect the resulting path diff and byte parity before retaining it. Do not apply a broad hi update when hi diff shows unrelated managed drift. A local-path Skills CLI install may create skills-lock.json with a machine-specific absolute source; do not retain that non-portable receipt. Do not hand-edit active skill copies as the source change unless a local-only experiment was explicitly requested.
The upstream create-spec template must keep a non-empty string title in frontmatter, and its quality-bar self-review must verify that field before saving a spec, especially when the destination is a routed Fumadocs tree.
Scope boundaries are stated directly in authored guidance and phase instructions. Work proceeds within the accepted goal; changes to requirements, weaker gates, or substantial implementation redesign require explicit user authorization. The owning phase records the blocker and exact decision needed.
Shared phase contracts treat behavior-changing implementation as RED/GREEN-audited work. create-plan records the RED/GREEN fields and one implementation_skill_guidance item for every implementation-applicable assigned skill. implement-spec forwards that guidance unchanged and records exactly one IMPLEMENTATION-NOTES.md evidence row per item with loaded, applied, or justified not_applicable status. Missing RED/GREEN proof or missing, extra, or contradicted skill evidence becomes a review finding. UI implementation tasks also need durable before/after screenshot asset links in IMPLEMENTATION-NOTES.md, then the same links in the PR body, PR comment, or PR-ready handoff.
The default verification pack colocates verify-behavior, create-verification-skill, and update-verification-skill; implement-spec remains in the default planning pack. hi update installs the shared verification skills and never writes the project-owned Surface Verification References, which its Registry Item does not record. verify-behavior remains available for deliberate orchestration, not top-level automatic discovery. implement-spec invokes its verify mode for visibly exercisable acceptance after task and runtime checks and before acceptance classification. When the selected scenario finds an Uncovered Surface, implement-spec invokes the creator for the required project-owned reference and completes its Reference Smoke Proof; when it finds Uncovered Behavior, implement-spec invokes the updater for the existing reference and proves the added drive path. It then resumes the original scenario. debugging-phase invokes reproduce for reported visible failures before hypotheses or fixes. Each applicable run writes ## Behavior Verification Evidence in IMPLEMENTATION-NOTES.md with Story and criterion, Ref, Channel, Scenario, Status, and Durable evidence or exact blocker; matching runtime evidence is cross-referenced rather than copied. review-phase audits this retained evidence against its frozen target and does not create missing implementation proof. The distributed source is pinned to immutable tag sync/create-architecture-1991389066f5 at wearedevpunks/skills@1991389066f56e7297bacc5fe2cafd3858ca0ee4.
Runtime validation is a separate task contract from review_mode. create-plan sets runtime_validation: required when acceptance depends on workers, queues, persistence, providers, tracing, deployment wiring, or a similar process or infrastructure boundary. It also records the supported runtime_target, required public and durable runtime_evidence, and provenance-scoped runtime_cleanup. implement-spec carries those fields into sequential and parallel worker briefs, exercises the supported public entrypoint first, and records correlated public plus durable evidence in IMPLEMENTATION-NOTES.md. If proof remains inconclusive, the exact blocker is recorded and the task and affected acceptance criterion stay blocked; cleanup may remove only resources proven to belong to the validation run.
Every plan classifies architecture_applicability as local or architecture-bearing from cumulative ownership evidence. For architecture-bearing work, create-plan applies the agnostic $frontend-domain-structure and $backend-domain-structure responsibility, public-boundary, and dependency theory to current repository evidence. It invokes $show-me while authoring every normative PLAN.md view: Target Ownership Topology, Declared Dependency Graph, Responsibility Acceptance Criteria, Architecture Waves, Public Seam Contract, and Migration Ledger. The views preserve exact owners, public entrypoints, allowed and forbidden edges, criterion ids and due waves, topology deltas, uncertainty, and every planned temporary seam. Architecture waves express ownership convergence and remain distinct from worker wave_boundary parallelism.
Each architecture-bearing task persists architecture_wave, behavior_owner, integration_surface, public_seam, topology_delta, forbidden_ownership, temporary_seams, and responsibility_acceptance_criteria. implement-spec forwards those fields unchanged, reloads the applicable frontend/backend theory, and uses $show-me to persist observed ownership, dependency, wave-delta, and seam evidence in IMPLEMENTATION-NOTES.md. A cumulative conformance checkpoint after every architecture wave checks the whole affected graph, all criteria due through that wave, prior-met criteria for regression, public-seam changes, and migration-ledger additions, removals, and expiry. Any violation blocks dependent waves. A target-contract change routes back through $create-plan; final closure requires zero topology and dependency drift, every responsibility criterion proved, every public-seam change declared, green focused validation, and an empty migration ledger. In-goal architecture drift remains blocking rather than becoming generic tech debt.
Plan-mode detail belongs in create-plan and the resulting PLAN.md, not in generated shared AGENTS.md prose. The installed .agents/AGENTS.md should state only the compact policy: subagent-based work is mandatory; use one active execution/implementation worker by default; use parallel implementation only when the user explicitly asks; if the subagent/thread set is full, close existing completed or finished subagents before retrying; use parallel readonly subagents whenever readonly discovery splits across independent questions, paths, or hypotheses.
The canonical shared prompt is installed verbatim into .agents/AGENTS.md as a Copied Artifact and contains only the global Philosophy, Writing style, Think Before Coding, Simplicity First, and Surgical Changes sections.
Root prompt specs (Built Artifacts under .devpunks/specs/prompts/) carry criteria and invariants for the setup agent to materialize from repo evidence; they are not final prompt bodies. The setup handoff should direct the next agent to author root AGENTS.md from the actual apps, packages, contracts, tools, and docs it finds. Root AGENTS.md is a concise, table-free repo router. It should provide a repo surface map, workflow routing, cross-surface contract rules, validation routing, and short source/URL/docs rules. Route loose oversized fog to finder-phase before bounded requirements-phase; route product design work with existing evidence to explicit design-phase; route accepted specs, issues, or plans to delivery-phase. Generated available workflow entrypoints include design-phase, while docs/workspace ## Skills tables exclude it with every other lifecycle phase.
Docs/workspace prompt specs own a different contract. The follow-up agent reads current Code Evidence and writes structure-first scoped standards: semantic folder and module responsibilities, placement and dependency direction, public/composition/generated boundaries, then applied coding or authoring conventions. Production source, passing tests, schemas, configuration and manifests, and behavior-enforcing generators or scripts count as Code Evidence; generated output follows its generator. When current patterns conflict, enforced constraints win, followed by the nearest stable module-family pattern.
Keep each scoped prompt lean. Put granular project invariants in .agents/rules, then keep one exhaustive Rule Registry pointer in each applicable scoped AGENTS.md. Every final docs/workspace prompt has one ## Skills table headed Skill | Exact trigger, with one row per selected non-phase skill and its installed SKILL.md path. It also visibly links opensrc/README.md; read that guide when the task depends on third-party library behavior. Complete every generated docs/workspace target, preserve root/shared guidance and existing project-authored nested prompts, create no deeper AGENTS.md, and verify every relative reference resolves.
Harness installs no wiki; project wiki files, including <wiki-root>/AGENTS.md, stay project-owned. Python dependency files (pyproject.toml, uv.lock, poetry.lock, requirements.txt, and Pipfile) participate in automatic pack detection; SQLAlchemy or Alembic selects the sqlalchemy pack.
The subagent manifest uses the final docs/workspace ## Skills rows as exact-invocation triggers. When a task explicitly invokes a row's skill, the worker reads the installed .agents/skills/<skill-id>/SKILL.md before editing; the installed skill remains authoritative for the workflow and checklist.
hi init and hi update post-command handoffs activate writing-for-agents, then rule-authoring, before creating or reconciling project-owned .agents/rules and scoped prompt pointers. Other agent-facing authoring paths activate writing-for-agents. Each surface points to the owning artifact and keeps detailed mechanics in the skill.
On the Init branch, $hi-cli remains a concise router to this Built contract: it names Code Evidence ordering, progressive disclosure, the complete skill table, and the Source Guide trigger, then defers the detailed criteria to each generated prompt spec.
The default docs pack uses writing-for-agents in place of writing-great-skills; the docs pack does not select writing-great-skills. The default misc pack installs show-me and wait-what into shared .agents scope only; application workspace scopes do not inherit them. Handoff has no show-me integration.
grilling is the sole generic contract for frontier scheduling, question shape, fact lookup, branch settlement, state restatement, and shared-understanding completion. requirements-grill adds domain and codebase pressure tests, including applicable topology, boundaries, dependency direction, seams, persistence, and verification design. It persists stable question ids, prerequisites, current-frontier membership, answer state, and confirmation state in <wiki-root>/content/docs/project/grilling/<topic>-grill-status.md and <topic>-grill-log.md. Accepted architecture is compiled before the spec through the requirements and architecture handoff; GLOSSARY.md remains glossary-only.
The requirements pack also installs the pinned imported domain-modeling skill. requirements-grill invokes it before questioning and keeps it active through closure, using the active grill glossary as working persistence. After the user accepts closure, the existing wiki-synthesis step promotes accepted terminology into the canonical routed glossary for the relevant bounded context. Downstream create-architecture, create-spec, create-plan, implement-spec, write-backlog, backend/frontend structure, architecture, and wait-what work reads that glossary before using domain terms. A downstream need to add, rename, merge, split, or redefine a term routes back through requirements-grill; downstream consumers do not silently mutate glossary authority. The imported skill remains the sole authority for modeling criteria and behavior; Harness owns availability, invocation, and persistence routing.
write-backlog is the sole physical Linear or GitHub writer for Product/Backlog Root → Product Area → Initiative → Epic → Story → required Task. Fog remains lateral provenance. Business Finder may project through Initiative, Functional Finder may project through Epic, and neither projects Stories or Tasks. Requirements Phase alone authorizes delivery-depth Story, Task, and blocker projection from retained OUT-### outcomes. Every write requires exact readback, and blocker validation rejects missing targets, self-edges, and cycles before return.
Before provider writes, write-backlog reads wiki authority and fresh provider state, reconciles stable provider and durable wiki identities, and previews every material hierarchy, roadmap, milestone, duplicate, or view change for explicit approval. Existing coherent objects are enriched before new ones are proposed. $show-me explains topology decisions and $wait-what repitches language that does not land; neither replaces authoritative ticket text, exact readback, or provider-native relations.
prototype now chooses one of two v1.2.0 formats from the question. Logic/state-model questions produce one self-contained HTML file that opens directly, exposes free-play buttons, renders full state after each action, and includes tabbed guided walkthroughs for hard cases. UI-shape questions produce three to five structurally different variants on one route, selected through ?variant= and a floating bottom switcher. Both remain throwaway evidence retained on a remote prototype/<slug> branch with an immutable commit pointer.
docs-ingest-phase is also the routed learning refresh loop. For durable bug or knowledge learning, scan the scoped routed learning area first, then choose keep, update, consolidate, replace, delete, or mark_stale. Canonical learning stays in routed wiki/project docs and must expose at least one future-use hook; memory notes are only compact routing aids for hard-to-discover gotchas.
When changing a phase wrapper, preserve resumable phase routing. The wrapper may name the lifecycle sequence, but it should choose the current gate from repo/tracker/artifact state before loading child skills. delivery-phase keeps SKILL.md as an entrypoint, routes through phases/router.md, loads one selected phases/*.md module, and writes handoff state before stopping or re-entering routing.
Keep business-finder, functional-finder, finder-phase, bug-discovery-phase, bug-resolution-phase, delivery-phase, design-phase, prototype-phase, requirements-phase, review-phase, and resolve-debt-phase explicit-only: their Claude frontmatter must set disable-model-invocation: true, and their Codex agents/openai.yaml must set policy.allow_implicit_invocation: false. A human chooses Business Finder or Functional Finder; root routing never starts Finder implicitly. debugging-phase and docs-ingest-phase remain model-invocable delivery delegates. Once explicitly invoked, Full Delivery continuously re-enters routing and invokes review as an authorized inner step, with at most three review cycles. Only an explicit user request enables HITL confirmation. Direct child-skill invocation still stops at that skill's boundary. Design and debt resolution must build and present a delivery brief, then stop for explicit user invocation of $delivery-phase instead of loading or running it.
At the review gate, the bootstrap loads phases/router.md, recomputes one route from current evidence, and loads exactly one flat gate: prepare-review, run-review, retain-report, or return-route. Each nonterminal gate writes its durable outcome, stops, and may re-enter the bootstrap; sibling gates never load each other. Delivery mode appends review-owned records to the validated caller-provided delivery handoff. Standalone mode uses one deterministic repository-local handoff derived from lineage and run identity. Cold resume reconstructs state from direct evidence, fresh artifacts, and the applicable handoff instead of transcript continuity.
The prepare gate normalizes either the smallest-certain Git/diff scope or a deterministic standalone plan, spec, or documentation bundle, recovers delivery counters, and freezes it. The run gate invokes $autoreview once for advisory candidates and verifies those candidates against the same frozen target while independent Standards, skill-adherence/scoped-skill, architecture, simplify, and Spec lenses inspect it. Priority affects report and triage order only; Standards and Spec remain separate. Validation stays readonly and no broader than accepted evidence requires.
A completed pass has one immutable report under apps/wiki/content/docs/project/reviews/. Every review mode requires a non-empty inclusive scope. Retention resolves reportCommitSha:reportPath to the actual commit-tree blob, requires its bytes to equal the local report exactly, and records containment only when the ancestry check returns boolean true.
Every finding records one explicit return_route. The aggregate route is derived centrally by fixed precedence: debugging, implementation, debt_follow_up, docs_ingest, then closeout. An empty finding set closes out; docs-only findings route to docs ingest; architecture debt can remain a secondary route beside an implementation repair.
Review remains readonly. A mixed repair-plus-debt route enters debt_follow_up first, captures the goal/spec-linked artifact exactly once, then resumes the preserved secondary debugging or implementation through durable post_debt_route. Primary capture atomically persists docs_ingest or closeout; the router consumes that durable state before artifact inference, without repeating capture or implementing debt. One delivery lineage permits reviews 1-3 and repairs 1-3. After fix 3, focused validation remains in repair epoch 3 until it passes, then delivery records clean continuation without review 4. External GitHub and Codex PR review are separate capabilities. The installed review-phase and delivery-phase skills own the detailed graph, schemas, freshness, and counter rules.
Install or repair the global operator skill through the verified lifecycle:
hi operator installThis latest-source lifecycle starts in CLI 5.0.1 and requires Skills CLI >=1.5.20. Once per operator command, Harness fetches canonical wearedevpunks/skills main, captures the fetched exact commit, and uses that commit plus one shared detached checkout to install and verify hi-cli. Every scope and readback in the command uses the captured identity and checkout content. Fetch, commit capture, checkout, or content-verification failure remains explicit and exits nonzero; Harness does not substitute its compiled skill. Unsafe paths, list failures, and missing, unsupported, unreadable, malformed, or timed-out Skills CLI evidence remain explicit failures rather than absence. A canonical listed hi-cli copy with mismatched immutable content is instead repairable outdated inventory.
Requirements and architecture handoff
The default requirements pack installs the source-backed brainstorm dependency, so fresh installs can run the architectural lenses used by grilling and architecture compilation.
requirements-phase runs requirements-grill → create-architecture → create-spec → write-backlog. The default planning pack includes the model-invocable create-architecture skill beside create-spec. Every delivery requires architecture, including small local changes. See the discovery report for the source evidence and accepted decisions.
After requirements closure, create-architecture reads grill status, then the decision log and required referenced evidence. It resolves stable question IDs and explicit supersession against the current glossary. Closed, confirmed decisions become <resolved-specs-root>/<domain>/<topic>/ARCHITECTURE.md, beside the future SPEC.md; architecture creation does not require an existing spec. Use Create Spec's routed wiki, legacy wiki, or docs/specs location and collision rules. Material gaps, contradictions, missing evidence, or new decisions return architecture-not-ready with exact missing items to requirements grilling. Preserve the prior canonical artifact until a complete replacement is ready. Parked items retain their owner and resume trigger.
The architecture records source paths, identities and revisions for status, log, glossary, and evidence, with question IDs beside the decisions they support. Preserve exact accepted library names and versions, utilities, helpers, APIs, file paths, algorithms, configuration, protocols, alternatives, and rationale. Mark accepted design, observed current behavior, and unknowns distinctly. An unspecified choice stays unspecified. Use concise ASD-STE100 Simplified Technical English and canonical glossary terms, with system context before detail. Apply the requested language standard directly; it requires no automatic invocation of the user-only wait-what skill.
Use show-me to explain the architecture within the artifact: a system map plus applicable flow, sequence, state, pseudocode, or file/call-tree views. Each view answers a distinct question and retains exact component, data, control, and ordering relationships. Put source anchors and a short causal explanation beside each view. Use the smallest useful views, without a diagram quota. Validate Mermaid with an available parser or renderer and record any validation limit. The handoff links the artifact and explains how its parts work together; visual clarity establishes neither acceptance nor implementation proof.
Retain the complete architecture and required planning-surface bookkeeping in a dedicated, path-limited commit through the approved remote mechanism. Verify the immutable blob URL and bytes before returning architecture-written with agent-ready readiness. This means the compiler has complete inputs and a retained artifact; it adds no human approval checkpoint. create-spec consumes and cross-links that retained architecture, incorporates its accepted constraints into requirements and acceptance criteria, and retains the resulting spec before backlog projection. ARCHITECTURE.md owns the detailed source-attributed design; SPEC.md owns resolved requirements, outcomes, and acceptance criteria. Both must agree with current grill decisions. Contradictions return to requirements grilling; neither artifact silently overrides the other. PLAN.md owns task order, files, worker assignments, and validation commands.
Give architecture blocks and flow steps stable selectors, with source grill Q IDs and evidence anchors, before spec codes exist. When create-spec assigns actual OUT-* and AC-* codes, it finalizes a many-to-many traceability map in architecture: spec code → architecture section/block/flow-step selector → source grill question anchor. Keep selectors stable and preserve every accepted detail. Record an explicit reason when a spec item has no structural counterpart. This enrichment changes traceability metadata only; a design change returns to requirements grilling. Retain the enriched architecture first, then let the spec reference its exact retained identity. Architecture maps spec codes without embedding a reverse immutable spec identity, avoiding a circular retention dependency.
Before any dependent delivery dispatch, verify the current retained ARCHITECTURE.md and SPEC.md pair, plus the existing backlog proof. This gate covers fresh delivery, resume, review, repair, debugging, closeout, and direct create-plan or implement-spec entrypoints. Missing, stale, or conflicting inputs return the exact gap to requirements-phase; legacy work and architecture_applicability: local have no exemption. Existing Context Pointer identity and freshness contracts carry the architecture through planning, implementation, and review. Timestamps alone do not prove currency. Architecture applicability controls the detail of plan convergence, while every plan reconciles the accepted pair. Verify that the map is complete and current, all selectors and Q anchors resolve, and every spec outcome and acceptance criterion has architecture coverage or an explicit no-structural-counterpart reason.
Brainstorm checkpoints in requirements grilling
brainstorm owns the complete architectural failure lens list. For software flows, it accounts for every lens against current evidence. requirements-grill invokes it initially and after every response set, including an incomplete frontier response, after recording supplied answers and before computing and persisting the next frontier or requesting closure. Prior lens evidence can be reused while its supporting facts remain valid; changed facts require fresh assessment.
Findings remain candidates for investigation and user confirmation. Resulting questions enter the existing grill queue with their prerequisites and answer/confirmation state; unresolved questions remain subject to the existing closure rules. The checkpoint does not itself accept requirements or close the grill.
Discovery And Output Contract
Executable help and generated command references come from one recursive registration graph. hi --help lists exactly init, update, diff, and check under "Baseline lifecycle". Other roots are commit-gate, operator, report, skills, tools, and upgrade; nested commands include operator status, operator install, operator update, operator migrate, deprecated skills rename, tools ensure, and commit-gate verify. hi scaffold and hi add are unknown commands.
Commands publish presentation-neutral OperationResult facts once through the root-provided presentation sink. The root selects interactive, redirected plain, or JSON output once, then owns rendering, stdout, stderr diagnostics, and exit status. JSON is non-interactive and emits one document. Redirected stdout selects plain mode even when stdin remains a TTY. Redirected init without --yes returns typed recovery guidance before prompting. Redirected plain output is deterministic and contains no ANSI. Prompt interaction owns terminal input and cleanup separately from result rendering.
Interactive output uses one compact open-rail grammar: ◇ starts a section, │ carries semantic
facts/actions, and └ closes the final row. Expected failures retain safe detail, recovery commands,
and retry evidence in the same form. The large gradient title is reserved for the interactive root
guide reached through bare hi, root hi --help / hi -h, or hi --wizard. NO_COLOR wins,
FORCE_COLOR=0 disables color, and other FORCE_COLOR values enable the palette selected by
HI_THEME or COLORFGBG.
Operator status and mutations keep the complete public IP322 evidence in that result, including scope, immutable identity, compatibility, legacy copies, project roots, guarded retries, reinspection outcomes, removal barriers, and reload guidance.
The developer renderer catalog is a review tool, not a command or package feature. It imports the production renderer in one direction and is excluded from dist and npm packaging. From the repository root, bun run cli:ui retains terminal.txt and index.html in a fresh temporary preview directory, prints the terminal atlas, and prints the preview's file:// URL. Use bun run --cwd apps/cli ui:catalog -- --check for self-cleaning validation or bun run --cwd apps/cli ui:catalog -- --output <run-owned-directory> for an explicit retained destination. See /docs/cli/commands/command-discovery and /docs/cli/product-foundation/cli-presentation-contract; those pages explain the flow and contract without becoming a second graph or renderer authority.
Run current source from the repository root with bun run cli -- <command>. This wrapper disables
CLI and operator-skill startup update checks so a stale global hi binary cannot masquerade as the
branch under test. bun run cli:entrypoints reads the executable command registry and prints every
root/nested path with its interaction mode, output modes, summary, and a ready-to-copy help command;
native --wizard, --completions, --version, --log-level, and --help actions are listed
separately.
Normal CLI startup treats cache-read and advisory background-spawn failures as non-fatal. They cannot prevent a non-JSON command handler from running.
Packs and detection
Repository detection ignores agent/cache/worktree directories such as .claude, .codex, .cursor, .opencode, and worktrees so hi init does not scan cloned repositories from local agent state. Inside a git work tree, detection and Recorded Shape TypeScript evidence also skip every path git ignores (.gitignore, .git/info/exclude, global excludes), so ignored build output, generated manifests, or stray sources never propose a Pack or add a workspace; untracked paths that are not ignored still count. Outside git, only the directory skip list applies. hi update re-detects the shape on every run but never edits settings, so remove a Software Scope for a deleted or now-ignored workspace from lint.scopes yourself. Pack detection also ignores selected wiki roots (apps/wiki, app/wiki, and wiki) so Fumadocs dependencies and generated wiki TypeScript do not select framework, frontend, or language packs by themselves.
Every installed repository receives the agnostic quality pack. Its testing skills have separate write boundaries: tdd owns production behavior changes and requires honest RED before production GREEN; make-tsuite owns automated software test portfolio audits and explicitly authorized test-only changes while production remains immutable. make-tsuite discovers test locations, execution mechanisms, tiers, and fixtures from the repository, including required delivery gates when present. Its TSuite name means test suite and does not imply TypeScript or any language, framework, execution mechanism, or delivery system.
Expo package detection proposes the expo pack when manifests list expo or @expo/* packages. The pack distributes the shared Expo skills for Expo app structure, EAS workflows, deployment, updates/observe, native UI, modules, web-to-native migration, and eval workflows.
Effect package detection proposes the effect pack when manifests list effect or @effect/* packages. The pack distributes effect for Effect v4 APIs, effect-service-design for service qualification, dependency-preserving and ready production Layers, test substitutes, and audits, effect-backend-structure for action-owned orchestration and integration-owned contracts, and effect-recoverable-actions for recoverable multi-step actions. It does not retain the superseded effect-authoring or effect-best-practices branches or route by installed Effect semver.
Prompt specs route Packs by scope. The root prompt spec lists every installed Pack. The shared (.agents) and docs prompt specs list the default Packs. A workspace prompt spec lists:
- the
defaultPacks whose catalogpromptSurfacesincludeworkspace-prompt-spec:debug,planning,quality,research,requirements,security, andsubagents.docs,misc, andverificationappear only in the shared, root, and docs specs. A Baseline published withoutpromptSurfacesin Pack meta keeps everydefaultPack in every workspace spec; - a
surfacePack only when that workspace shows its signals:frontendfor anextjsorreactworkspace;backendfor abetter-auth,drizzle,elysia, ortrpcworkspace, or one whose path has anapi,backend,server, orservicessegment or whose package name has anapi,backend,server, orservicesegment.hi initproposals use the same signals repository-wide; - every Pack whose detected technologies or languages match the workspace.
The docs prompt spec (.devpunks/specs/prompts/docs.md, targeting docs/AGENTS.md) is built only when the docs Pack is selected and the Recorded Shape has docsRoot, meaning a docs/ directory exists and git does not ignore it. A repository without docs/ gets no docs spec and no handoff row for it.
Prompt target discovery includes authored scoped AGENTS.md files outside managed/generated directories even when they are not package workspaces. Keep root, .agents, docs, and package-manifest targets deduped, but preserve real non-package surfaces such as infra/opentelemetry/AGENTS.md so Built prompt specs follow the consumer repo shape instead of Harness placeholder paths.
hi update writes opensrc/*.md guide cards for detected package sources that agents commonly need to inspect. These files are indexes for opensrc, not vendored source. The package source remains in the global ~/.opensrc cache; the card tells the next agent to run opensrc path --cwd . <package> or a fallback repository lookup before reading internals. The Effect card resolves Effect-TS/effect main first as the Effect v4 authority and uses the project-pinned package only for exact installed-version behavior.
hi update derives lint output from saved Software Scopes and local framework evidence. Each selected owner receives one effective oxlint.config.ts composing supported Ultracite presets, applicable overlays, and preserved Project Lint Policy; see managed lint scopes and policy. The Registry carries every declared lint asset. The TypeScript pack vendors Anti-Slop as a local JavaScript plugin and adds @oxlint/plugins to its selected JavaScript/TypeScript owners. Applicable assets and plugin namespaces remain local to their owner, with all referenced rules registered. Exact package-name selectors preserve package-only policy without leaking repository-specific exclusions or rule overrides to other consumers; the CLI uses this for shipped-asset exclusions and backoffice for function-style and Vitest exceptions. Preset-local rules stay in the selected preset’s overrides, optional overrides remain optional, and formatter-stable TypeScript emission keeps generated bytes reproducible. Supported missing dependencies are planned in the owning package context; broad guidance packs do not enroll additional lint owners.
Managed lint excludes wiki roots: Harness introduces no wiki lint config, plugin set, or managed lint command. Existing authored wiki configuration remains project-owned. hi update removes only stale Copied lint files it recorded; exclusion does not establish deletion ownership.
The Effect pack keeps effect-no-barrel-imports and separately imports the experimental named recommended preset from @effect/tsgo/oxlint-presets. Its native patch is release-coupled: the supported scaffold tuple is exactly @effect/tsgo@0.39.0, oxlint@1.80.0, oxlint-tsgolint@7.0.2001, and typescript@7.0.2; the official patch rejects Oxlint 1.81. The root package is the sole owner of the effect-tsgo patch --no-typescript --oxlint prepare segment; workspace manifests receive no duplicate hook. The native plugin is unavailable on musl Linux. If an editor reports each issue twice because both the Effect language server and Oxlint publish diagnostics, disable one editor diagnostic source. Do not mutate tsconfig.json to hide that editor overlap.
The frontend pack includes improve-mobile-frontend for scoped mobile-web interaction, viewport, safe-area, gesture, browser-chrome, accessibility, and real-device validation guidance.
The Expo pack includes animate-expo. The frontend pack includes animate and review-animations. These imported Emil Kowalski skills carry their upstream MIT license attribution in the Registry. The React pack composes Oxlint's React Compiler-powered correctness rules into the owning workspace config, so compiler validation failures appear in its normal lint command. Existing projects receive these additions through hi update when the Pack is in settings packs: review hi diff, apply hi update, then confirm with one hi check --json.
Pipeline architecture, security, and Pulumi boundary
The security pack routes CI/CD work by intent:
architect-pipelinedesigns or changes repository-aware, provider-neutral CI/CD, infrastructure-deployment orchestration, release identity, artifact publication, retries, and promotion theory.audit-cicd-securityhandles an explicit security audit, threat model, or pipeline-hardening review and remains read-only unless the user authorizes remediation.security-best-practiceshandles explicit application-code security requests for its supported languages and frameworks.
architect-pipeline owns the orchestration theory around infrastructure deployment: stage dependencies, preview and approval gates, artifact handoffs, retries, promotion, and failure recovery. When a pipeline invokes Pulumi, it may call an existing repository command and describe the required gate, but Pulumi-specific implementation belongs to the Pulumi pack. pulumi-overview, pulumi-best-practices, pulumi-component, pulumi-esc, and pulumi-automation-api own Pulumi APIs, resource and component behavior, state, registry and provider details, authentication, ESC/OIDC, and embedded automation. Do not move those details into provider-neutral pipeline guidance or invent Pulumi configuration while designing a pipeline.
Edit hook formatting
OpenCode auto-loads the hooks from .opencode/plugins; its after hook reads edit, write, and apply_patch arguments from the OpenCode input object. Before formatting or linting, the edit hook reads .devpunks/installed.json and skips every recorded Copied or Built path. When the record is missing, unreadable, or invalid, it treats nothing as managed and never blocks the edit. Lexical aliases are rejected, and real paths must remain inside the repository. Mutating Oxfmt and Ruff work consumes captured bytes through stdin while the original path remains the stdin filename for parser and config resolution. Output is written and file-synced to a private same-directory stage before the live target is atomically moved to a private claim. The hook validates the claimed identity and digest, then exclusively links the staged inode into the absent pathname. After publication, recovery never withdraws an existing target; it restores the exact original claim only when the target is absent. If Ruff lint fails after successful formatting, the staged formatter bytes are preserved and the lint failure is reported. hi diff compares managed bytes and never runs repository formatters; a manual repository-wide format can create legitimate local-edit drift. Python .py edits route through Ruff with uv run --project <python-project>; nearest pyproject.toml wins. Python uv workspaces that include package.json only for tooling metadata stay on the Python lint route. Managed JS/TS linting resolves the selected file owner and its explicit effective config; see managed lint scopes and policy.
Wiki
The Registry installs no wiki: wiki setup belongs to the owning project, and hi never creates or aligns a wiki. This amends Registry Baseline SPEC OUT-004 and OUT-006 by user decision on 2026-09-28. Detection records an existing wiki root (apps/wiki, app/wiki, or wiki) in the Recorded Shape only to keep it out of workspaces, Packs, and Software Scopes. Projects that keep a Fumadocs wiki, such as this repository, own its sync script, routes, and checks.
Repository CI and release verification
Harness Intelligence's project-authored active wiki sync adds a CI entrypoint guard. Ordinary CI/deployment package wrappers build from committed routed content without applying or pruning projection output; explicit local sync still applies changes, and --check still validates drift when CI is set. The hashed @punks/wiki#check:content Turbo task runs the non-mutating full projection check before the wiki build, with root docs/** included in its inputs. This ordering prevents generation from hiding drift and keeps the check separate from concurrent .source generators. The committed content/docs/project/specs/.source-projection.json inventory identifies source-backed spec paths: sync prunes only inventory-owned paths missing from apps/wiki/specs, while routed-only specs outside the inventory remain canonical. If the inventory is missing, the first apply records current source-backed paths without deleting unproven routed content. Turbo adds build-scoped CI only to @punks/wiki#build. Because a package-qualified task override replaces the generic task fields instead of merging them, @punks/wiki#check-types must redeclare the generic check-types environment exactly: CI, NEXT_TELEMETRY_DISABLED, and TZ. This duplication is required, and the task depends on the wiki build. This avoids unrelated build-cache invalidation and concurrent .source generation.
For app-root Vercel monorepo deployments, use Vercel native affected-project skipping on each project instead of generated ignoreCommand fallbacks. The ignored-build hook still creates noisy deployments and can consume build/concurrency accounting before it exits, while native skip uses the workspace dependency graph and project root directories.
Keep the private root package free of workspace:* dependencies. Root orchestration invokes package tasks with Turbo filters and does not need a dependency on @punks/cli; that edge makes CLI-only commits affect every app-root deployment. The repository contract checks the affected package sets: CLI-only selects CLI, wiki-only selects wiki, and scaffold changes select scaffold plus its API and CLI dependents.
Behavior-contract CI uses Turbo for reusable work. test:ci runs the root behavior/static contracts and check:repo before scope selection, so root configuration and lockfile checks execute in both full and affected modes. Its generic test wave excludes @punks/cli#test and @punks/api#test; the separate check, check-types, and build wave still runs CLI and API verification once. API tests remain covered exactly once through the @punks/api validate:runtime-product dependency. Pull requests with trustworthy refs and workspace-only changes use affected cached tasks; unsafe scope falls back to the full graph. Explicit inventories route repository and CLI tests through portable, capability-keyed, or fresh tasks. The shared wrappers and Turbo graph, rather than workflow-authored test commands, own exact-once routing, task identity, and cache policy. CLI Vitest hooks and tests use a finite 45-minute suite ceiling; subprocess, network, and lifecycle guards retain narrower bounds. For bounded two-worker local feedback only, first run bunx turbo run build --filter=@punks/cli --concurrency=2, then bun run --cwd apps/cli test:parallel; the package test script remains serialized. The runtime-product command stays outside affected pruning so API runtime evidence exists before validation. Runtime-product setup probes select 1 through the exact host DATABASE_URL before db:migrate and redacts migration diagnostics; this distinguishes host-port readiness races from migration failures without leaking connection details. Playwright and the backoffice browser suite remain explicit and outside the default CI graph. Backoffice deployment variables belong only to @punks/backoffice#build cache inputs. Only @punks/wiki#build adds CI as a build-scoped input. Because a package-qualified task override replaces rather than merges the generic task fields, @punks/wiki#check-types redeclares the generic environment exactly as CI, NEXT_TELEMETRY_DISABLED, and TZ; the duplication is required. The task depends on the wiki build.
CLI release verification cache and trust boundary
Install the repository-owned staged and pre-push gates once per checkout:
bun run hooks:installThe pre-commit hook reads the linked worktree's staged index. It ignores unstaged bytes, accounts for both sides of a rename, and runs check plus check-types only for selected workspace owners. A root or unknown path adds check:repo and expands verification to all workspaces. The pre-push hook runs the CLI's local release:classify check with CI=true, LANG=C, LC_ALL=C, NODE_ENV=test, and TZ=UTC; override its default origin/main and HEAD refs only with HI_RELEASE_BASE_REF and HI_RELEASE_HEAD_REF.
Before starting classification, pre-push clears the repository-local variables
reported by git rev-parse --local-env-vars so Git commands in the classifier's
detached worktrees resolve their own checkout. PATH, release-ref overrides, and
unrelated environment settings remain available.
Verification has three explicit cache classes:
portablehashes every result-affecting source, fixture, dependency, controlled environment, Node identity, and Bun identity. It can replay across macOS and trusted Ubuntu only when the outputs are byte-equivalent.capability-keyedadds the observed operating system, architecture, filesystem behavior, and exact tool identities needed by host-sensitive filesystem, subprocess, signal, native, or container work.freshalways executes. It is limited to the smallest witnesses for current scheduling, contention, elapsed time, external lifecycle, or external mutation, and each bypass records its reason.
The CLI title inventory currently classifies 442 tests exactly once: 119 portable, 317 capability-keyed, and 6 fresh. The repository graph separately classifies 87 targets as 12 portable, 72 capability-keyed, and 3 fresh. Missing or duplicate assignments fail the inventory contract. Signed development entries can be shared by authenticated local development and trusted same-repository pull requests only when the complete identity matches. Forks receive no cache credential. Protected release evidence uses a separate signature authority and rejects development artifacts. Local and static contracts prove policy; the CI run remains the provider-backed evidence for its exact tree.
Run the same local release classifier directly from the repository root:
env CI=true LANG=C LC_ALL=C NODE_ENV=test TZ=UTC \
bun run --cwd apps/cli release:classify -- --base <base-ref> --head <head-ref>Candidate Evidence is a provider-produced PR verification receipt. .github/workflows/behavior-contract.yml writes it after trusted pull-request static and affected verification succeed, recording the repository, workflow run, pull request, head commit, tested tree, and successful conclusion. Local release:classify validates release selection and reviewed intent for the supplied refs; it does not mint Candidate Evidence.
Release kind follows only the changed changelog paths: BASELINE_CHANGELOG.md selects baseline, CHANGELOG.md selects npm, both select mixed, and neither selects none.
Recovery never changes that selection. A legacy-named published-without-candidate-evidence declaration is readback-only for an exact historical first-parent commit whose selected products were already published; it creates no publication artifacts and refuses partial state before any provider mutation. See CI verification and publication for the recovery and Registry readback contract. Current and future publication performs no current or historical Candidate Evidence lookup.
Semantic and literal artifact differences remain diagnostics; they do not select a product. Unknown changed-path authority or unavailable artifact authority still fails closed.
Before completing release-bearing work, run classification against the intended base and head. A selected baseline requires non-empty reviewed BASELINE_CHANGELOG.md notes (Unreleased or an exact version heading) and a Compatibility: CLI range, which becomes the Baseline's cliVersionRange. Selected npm requires a new package version and matching non-empty CHANGELOG.md entry. Mixed candidates satisfy both branches. Completion requires the classifier to pass.
Convergence walks first-parent main history from a durable provider anchor and stops at the earliest incomplete reviewed release. It never skips, coalesces, cancels, or reorders reviewed versions unless an exact current-tree recovery declaration records the evidence-bound disposition. A mixed release converges the Registry Baseline before npm. Each product reads back its published state (the public Registry bytes and latest for a Baseline; package, aliases, and GitHub release for npm); a retry resumes only missing or stale state. Publication, alias mutation, and GitHub release mutation remain uncached external effects.
.github/workflows/release.yml is active protected authority. Pull requests exercise refusal contracts, while .github/workflows/behavior-contract.yml retains Candidate Evidence as verification diagnostics. A main push creates an immutable repository, commit, and tree input directly from its checkout; publication eligibility does not search prior pull-request runs or artifacts. The credential-free plan independently revalidates changelog selection and reviewed intent before Production can run. Release evidence and production each have a 45-minute job ceiling. Production owns the durable provider anchor and the Registry publication secret BLOB_READ_WRITE_TOKEN; its id-token: write permission supplies npm Trusted Publishing OIDC with provenance. No Vercel deployment credential is present. Provider readback from the completed run is the final convergence proof for that exact tree.
Repository package type-check scripts use ordinary tsc, which is owned by @typescript/native 7.0.2. The typescript name aliases @typescript/typescript6 so framework and analysis tools retain the TypeScript 6 API. Bun currently installs the wrapper's internal npm alias as a self-reference, so patches/@typescript%2Ftypescript6@6.0.2.patch deliberately loads the explicit root @typescript/old 6.0.3 package. Preserve the package and lockfile patch authority until Bun resolves that alias without a cycle.
hi tools ensure
Use hi tools ensure for an explicit refresh of the selected auto-managed toolchain. The installed Baseline's tool list owns a trusted latest install target for every auto-managed tool, and the command invokes that target even when the binary is already on PATH. Manual platform tools such as gh, az, and glab are excluded from automatic installation and upgrade; the command validates them and reports installation guidance when missing.
In an installed repository, hi tools ensure installs the tools named in settings requiredTools and in the installed Registry Items. Outside one, it validates the default Harness toolchain. It is not read-only. Install and validation commands run with the caller's command environment, including PATH, so a tool already on the caller's PATH is found (#239). The opensrc contract remains >=0.7.2; hi tools ensure refreshes through opensrc@latest.
After refreshing the agent-browser package, hi tools ensure still runs browser provisioning when no configured or supported browser exists. A configured executable or an existing Chrome, Chromium, or Brave installation skips the browser download.
For agent-browser, validation should skip the browser install step when a supported Chrome, Chromium, or Brave executable is already installed. If the agent-browser command itself is missing, the CLI may still install the package, then skip only the redundant browser download.
Skills CLI subprocesses are interruptible. Timeout and fiber interruption terminate the owned process tree, using a dedicated process group on POSIX and the owned tree-termination path on Windows. Cleanup waits for exit, uses bounded escalation, and preserves cleanup failure plus its raw cause instead of reporting a successful interruption with surviving descendants. The final CI follow-up bounds that cleanup under load as well as in focused interruption fixtures.
hi report
Use hi report for reusable Harness friction: stale skill guidance, broken CLI/docs behavior, setup pain, or workflow/tooling problems maintainers should triage.
Reports submit raw issue context to /api/reports; ReportSubmission.Service.submit is the sole report use-case seam. The API validates relevance, rejects trolling/profanity/non-coding reports, and generates deterministic fallback title/body for public CLI submissions. GitHub-backed delivery first persists an operator-independent pending row under a deterministic key produced through the injected identity-hashing service. Short claim and finalize transactions surround provider work without holding a database transaction open during GitHub I/O. Concurrent and retried submissions converge on that row using the original raw report identity, dedupe against open harness-report issues, and repair delivery from an existing matching issue when needed. Drizzle retry claims only unfinished delivery. The memory repository follows the same occurrence boundary: it reuses unfinished delivery only, while a completed identical report or a later createdAt creates a fresh occurrence. Disabled or omitted GitHub integration leaves a valid report stored locally without provider reads. Provider and AI SDK work is aborted when the submission is interrupted, and interruption-safe finalization releases an active claim for retry. hi report records every accepted response locally; a response without githubUrl is successful local-only storage, and human output omits the URL row and review action. AI SDK + OpenRouter metadata generation remains limited to authenticated backoffice operator sessions. Use --message, --area, --skill-pack, --command, --expected, --actual, --steps, and --labels to make the issue descriptive and classifiable. Keep project product backlog work out of reports unless maintainers explicitly promote it.
Backoffice ingests CLI telemetry and successful harness report submissions.
hi operator
Starting with CLI 5.0.1, use hi operator status, install, update, or migrate to manage the independently installed hi-cli operator skill from the latest canonical source. At operator-command start, Harness fetches canonical wearedevpunks/skills main once and captures FETCH_HEAD as an exact commit. That commit and one shared detached checkout become the target for every scope, mutation, and readback in the command, so concurrent movement of main cannot split target identity within one run.
Agent and harness target selection belong to Skills CLI. Harness invokes each inspection or mutation without prompting for an agent, detecting the active host, passing --agent, or reading HI_OPERATOR_AGENT. The same contract applies in interactive, plain, JSON, redirected, and recovery flows. Skills CLI detects supported harnesses and, when selection is needed, presents its own choice to the user. Its JSON inventory aggregates provider agent bindings for a scope and skill into one canonical path; Harness uses that path to byte-verify the returned copy and does not enumerate a separate copy for every harness. The path is inspection evidence, not retained copy identity.
status inspects the canonical global and current-directory project records returned by Skills CLI. Inside a project, the project-local record is effective and the global record remains visible as shadowed. This precedence also applies when the local copy is outdated, so it cannot be hidden by a verified global fallback. A canonical hi-cli copy whose immutable content identity differs from the target is retained as unverified, outdated inventory; status warns and recommends hi operator update. Outside that project, the global copy remains the fallback. Verified records report source, version, revision, compatibility, aggregated provider agents, scope, and project root when applicable. Unsafe paths, list failures, and missing, unreadable, or malformed evidence remain detection failures, never absence or outdated inventory.
install delegates the global target choice to Skills CLI, even when a project-local copy is already effective. update addresses each detected hi-cli scope and fails with install guidance when no canonical copy exists. After a successful-looking write, reinspection uses Skills CLI's returned canonical path to byte-verify immutable content, then records the compatible range, scope, project root, and aggregated agents. The path is not part of retained identity, so a same-content path change is acceptable after re-verification. A persistent mismatch remains unverified and the command exits nonzero. This proves the returned canonical copy, not every provider target behind Skills CLI fan-out. Independent scopes are not rolled back: a mixed update prints every verified, failed, or unverified outcome, retains successful work, and exits nonzero.
The operator skill target's CLI compatibility range follows the running CLI major, >=<major>.0.0 <<major+1>.0.0, so hi operator under CLI 6.x resolves and records >=6.0.0 <7.0.0 instead of refusing the target as incompatible (#240).
Operator writes require Skills CLI 1.5.20 or newer. The adapter initializes a scoped temporary Git checkout, adds the canonical shared-skills origin, and performs one depth-one fetch of origin main. It resolves FETCH_HEAD^{commit}, records that exact revision, and checks out the fetched commit detached. The command reuses this checkout as the source for every affected scope and verifies installed and readback copies against its exact skills/agnostic/cli/hi-cli content. A fetch, commit-capture, checkout, or content mismatch refuses the affected operation without falling back to the compiled skill, and the checkout is removed after success, failure, timeout, or interruption.
Failed or unverified writes print a guarded retry that freezes the target, scope, and cwd, then re-enters post-write verification. Recovery commands do not require HI_OPERATOR_AGENT; on Windows, mutation and migration retries use powershell.exe -EncodedCommand with UTF-16LE bytes so cwd and token remain literal across shells. The recovery command emits no diagnostics. Inspection failures rerun the operator lifecycle without converting unknown state into an install. Before unscoped removal in a legacy scope, migrate verifies the canonical replacement and requires its provider-reported agents to cover all provider-reported legacy dp-cli bindings. Missing coverage, barrier loss, target drift, or a persistent post-write mismatch refuses removal, preserves dp-cli, and prints a guarded migrate retry. Skills CLI owns removal fan-out after that coverage gate. hi skills rename remains a deprecated delegate to hi operator migrate; it owns no separate lifecycle or rename policy.
Any observed successful write prints guidance to reload or reactivate $hi-cli, including partial-success cases. Operator commands do not inspect .devpunks, change Managed Artifacts or Packs, resolve or publish Baselines, upgrade the executable, or synchronize the shared skill source. Use hi init/hi update, Registry publication, hi upgrade, and the shared-skill source-first sync workflow for those separate jobs.
hi upgrade
Use hi upgrade to update the CLI executable itself. The command checks the selected npm dist-tag, detects whether the current global install came from Bun, pnpm, npm, or Yarn, and runs the matching global reinstall command. Detection resolves the invoked bin symlink first, so Bun global layouts such as bin/hi -> ../install/global/node_modules/@punks/cli/dist/index.js are treated as Bun installs instead of generic unknown commands. It forces minimum release age to 0 for the upgrade run so a just-published CLI release can be installed immediately. Public success facts retain safe command-environment key names with <redacted> placeholders. Executable recovery commands derive only the product-owned npm_config_minimum_release_age=0 pnpm override or YARN_NPM_MINIMAL_AGE_GATE=0 Yarn override from the detected package manager, including failed executions whose result carries no environment map. Arbitrary keys such as registry tokens are omitted and inherited from the operator environment without being printed. Use --tag next for prerelease channels and --force to reinstall the selected tag even when no newer version is detected.
Interactive hi commands also check for a newer CLI before running. When an update exists, hi asks for y/n confirmation, installs through the detected package manager, and exits so the operator can rerun the command with the upgraded binary. The prompt is skipped for help/version output, CI, non-interactive shells, and HI_NO_UPDATE_CHECK=1.
hi -v is equivalent to hi --version.
Release Bookkeeping
CI verification and publication owns protected release publication, Registry publication, recovery, and provider readback.
Changing CHANGELOG.md selects npm publication. Every selected npm release needs one section named for the reviewed package version before it can become eligible. Put user-facing notes under categories such as ### Changed and ### Fixed; publish metadata can stay above those categories for the docs changelog. Unreleased notes describe pending work but do not supply release intent. The classifier and candidate gate verify the version and entry before authority is granted.
The installed npm command should not require Bun at runtime. Bun is a repo build/publish dependency only. Release validation should include the built dist/index.js under node, plus a sanitized npm-package install into a temporary prefix, then run hi and hint help commands from that prefix.
Registry-only releases change only BASELINE_CHANGELOG.md. The release workflow runs registry:build and registry:publish (rule HI-REPO-002); an operator publishes by hand only from a clean worktree with bun run --cwd apps/cli registry:build then bun run --cwd apps/cli registry:publish, and verifies the result by reading back the public Registry.
Local CLI verification and diagnostics
Run current source from the repository root with bun run cli -- <command>. The complete local CLI suite remains bun run --cwd apps/cli test; CI selects disjoint capability tasks as described in CI verification and publication. To test against a local Registry, build one with bun run --cwd apps/cli registry:build -- --out <dir> and point the CLI at it with HI_REGISTRY_URL=file://<dir>.
Ordinary bun run --cwd apps/wiki check begins with the non-mutating content projection check, before lint and formatting. When an intentional source-doc change creates drift, apply the complete projection with node apps/wiki/scripts/sync-content.mjs, review the generated diff, and rerun the ordinary check. CI remains strict and never applies this projection.
Historical: before CLI 6.0.0
CLI 5.x distributed Baselines as GitHub release artifacts promoted through the control plane, with a bundled fallback in the npm package. It recorded managed bytes in a Scaffold Manifest with content hashes, compiled a Context Plan file, projected harness files with sync-subagents.mjs, validated updates in a temporary Validation Candidate with Prepared Installation and Validation Result caches, archived replaced files, and wrote a Commit Gate lifecycle receipt. Commands were hi scaffold, hi ensure, hi check, and hi update --check|--write. CLI 6.0.0 retires all of these; the migration removes their files. The earlier behavior and evidence remain in git history and in the issue 215 and issue 197 records. Its gates (validate:consumer-repositories, validate:cutover-repeat, the cache-reuse witness workflow, and the CLI test:update suite) are deleted; end-to-end coverage now lives in apps/cli/src/registry/lifecycle.e2e.test.ts.