1000skills
v0.10.0
Published
Typed authoring SDK and deterministic runtime for repository agent harnesses
Readme
Repository policy that agents can execute
1000skills is a typed authoring SDK and deterministic JSON runtime for repository agent harnesses.
Authors describe routes, required context, file-class checks, overlays, and evaluations in a closed TypeScript module graph; runtime commands consume generated harness-v5 and eval-v5 JSON and never execute that source graph.
It ships as one ESM-only npm package for Node 22.12 and later.
Install
npm install --save-exact 1000skills
npm exec -- 1000skills harness initThe equivalent pnpm commands are pnpm add --save-exact 1000skills and pnpm exec 1000skills harness init. With Yarn, use yarn add --exact 1000skills and yarn 1000skills harness init.
init never overwrites starter files. When package.json exists, it wires missing harness package scripts from the public command catalog, including route, sync, diagnose, trace, complete check, and changed-file checks.
It also replaces script values that invoke the retired .agents/router/ implementation or exactly match a legacy command declared by the catalog, such as 1000skills harness changed without forwarded file arguments. Other existing script values are preserved.
Run A Bounded Goal
The package also installs goal-loop, a bounded foreground supervisor for agent work. A strict JSON manifest locks the objective, workspace, adapter, acceptance commands, and resource limits before the first attempt.
goal-loop run --spec ./goal.json
goal-loop status <goal-id>
goal-loop doctorAgent output is only a candidate. goal-loop records append-only lifecycle events and reports succeeded only after locked acceptance commands pass.
Foreground mode owns and terminates the complete process tree on timeout or interruption.
Background, resume, external-stop, and human-answer controls are not exposed. They remain gated on verified private-worker ownership, restart reconciliation, panic-stop, and at-most-one-process tests.
The manifest schema is published as 1000skills/schema/goal-loop-manifest.json, and programmatic types are exported from 1000skills/goal-loop.
The contract
harness.config.ts + .agents/harness/*.ts
│
check · snapshot · compile twice
▼
.agents/harness.json + .agents/harness.evals.json
│
route · check · changed · inspect · diagnose · explicit traceCompilation validates the closed source graph, strict types, ownership, membership, discovery, filesystem references, context budgets, collisions, and eval expectations. Matching fingerprints bind the generated pair; artifacts contain no timestamps, absolute paths, environment data, or secrets.
| Contract | Current value | | -------------------- | ------------: | | Harness schema | v5 | | Eval schema | v5 | | JSON output envelope | v5 | | Trace protocol | v2 |
package.json is the source of truth for the package version.
Keep the control plane small
harness.config.ts composes repository policy; it is not a second architecture document or a source-file inventory.
- Routes are mutually exclusive primary work surfaces with meaningfully different context.
- Overlays add concerns that can coexist with a primary route, such as testing, review, security, motion, or routing audit.
- File classes map changed paths to verification. Start with one baseline and add only source-backed exceptions.
- Guides and skills own durable knowledge. The config points to them instead of repeating them.
Review the shape when a harness grows beyond roughly a dozen primary routes, repeats effective route context, uses dense trigger lists, or defines routes with no context delta. harness check and harness diagnose report these as complexity warnings.
File-class counts have no complexity threshold because repository verification taxonomies vary legitimately.
Author a harness
Keep the root entrypoint small and put human-owned authoring modules under .agents/harness/:
// harness.config.ts
import { defineHarness } from "1000skills/harness";
import { author } from "./.agents/harness/author.ts";
export default defineHarness(author);// .agents/harness/author.ts
import type {
HarnessAuthoringContext,
HarnessAuthoringResult,
HarnessOwner,
} from "1000skills/harness";
const ids = {
bugFix: "bug-fix",
fallback: "fallback",
} as const satisfies Record<string, string>;
export function author<Owner extends HarnessOwner>(
h: HarnessAuthoringContext<Owner>,
): HarnessAuthoringResult<Owner> {
const policy = h.context.file("AGENTS.md", "Repository policy");
const testing = h.reference.guide({
id: "testing",
path: ".agents/guides/testing.md",
});
const fixBug = h.reference.skill({
id: "fix-bug",
path: ".agents/skills/fix-bug/SKILL.md",
});
const typecheck = h.command.packageScript({ id: "typecheck", script: "typecheck" });
const bugFix = h.route.define({
id: ids.bugFix,
priority: 80,
skill: fixBug,
triggers: {
keywords: ["bug", "failing", "regression"],
intents: ["fix", "repair"],
paths: ["src/**"],
},
mandatory: [
h.context.include(fixBug, "Use the repair workflow"),
h.context.include(testing, "Add regression coverage"),
],
});
const fallback = h.route.fallback({ id: ids.fallback });
const source = h.fileClass.define({
id: "typescript",
patterns: ["src/**/*.ts", "src/**/*.tsx"],
recommendations: h.command.recommend({
command: typecheck,
reason: "TypeScript changes require type checking",
}),
});
const bugEvals = h.eval.routeCases({
route: bugFix,
skill: fixBug,
mandatory: [policy, fixBug, testing],
cases: [
{
id: "bug-fix-render",
prompt: "Fix the failing render test in src/Button.test.tsx",
},
],
});
const fallbackEval = h.eval.case({
id: "fallback-general",
prompt: "Explain the repository",
expect: {
route: fallback,
mandatory: [policy],
},
});
return {
core: { mandatory: [policy] },
routes: [bugFix, fallback],
fallback,
fileClasses: [source],
evals: [...bugEvals, fallbackEval],
packageManager: "npm",
};
}The public authoring surface centers on defineHarness, opaque HarnessDefinition, owner-generic HarnessAuthoringContext, and HarnessAuthoringResult.
HarnessHandle supports owner-preserving generic utilities; named handle, input, and scalar/batch case contracts remain public so declaration consumers never depend on hidden types.
Typed handles catch kind mistakes in TypeScript; compilation catches ownership, membership, discovery, filesystem, section, link, collision, budget, and eval errors. Normalized runtime models remain private.
Explicit reference inputs may declare authority: { files: ["path", ...] }. This applies to h.reference.guide, h.reference.skill, h.reference.helper, and h.reference.adapter. Authority files are securely resolved repository-relative inputs whose fingerprints are stored with that reference.
They are not loaded as routed context, and no diagnostic judges whether either file's prose is correct.
Changing authority bytes, registering or removing authority tracking, or changing its source set requires semantic review. Normal write-mode sync refuses to replace recorded fingerprints until the reviewer runs 1000skills harness sync --accept-authority.
Unchanged authority continues through ordinary sync, while initial authority registration also uses the explicit acceptance path.
The compact authoring families are h.reference.guides, h.context.namedSections, h.command.packageScripts, h.command.recommend, h.sourceSet.define, h.sourceSet.harnessInputs, h.eval.routeCaseMap, and h.eval.changedCaseMap.
recommend accepts one { command, reason } object or a readonly array of those objects and always returns a fresh mutable handle array, including for empty input.
h.sourceSet.define({ id, patterns }) returns an owner-branded reusable static pattern handle.
Route and overlay triggers.paths, fallback-only intentionalPaths, and file-class patterns accept it beside literal strings, flatten it at authoring time in first-occurrence order, and emit only literal patterns; source-set identity does not enter generated artifacts.
Primary route inputs reject intentionalPaths; every generated fallback route contains its canonical string array, including []. Static sets require a safe unique ID and at least one nonblank string pattern.
They cannot contain another source-set handle, and a private scoped set must be exported and imported before another scope can use it.
h.route.overlayGroup({ id, mandatory?, variants }) keeps one conceptual concern within the overlay complexity budget while giving each source-backed trigger family its own mandatory context.
Variant record keys become stable flat overlay IDs in <group>.<variant> form; the returned variants map and overlays array contain ordinary owner-branded overlay handles.
Group-common context is prepended to each variant, matching variants compose additively, and runtime output, eval expectations, coverage, inspect, and trace use the flat IDs without a second selection mechanism.
const externalFetch = h.route.overlayGroup({
id: "external-fetch",
mandatory: [externalFetchSecurity],
variants: {
"media-proxy": {
triggers: { paths: [mediaProxySources] },
mandatory: [mediaProxyApi],
},
pinterest: {
triggers: { paths: [pinterestSources] },
mandatory: [pinterestContract, providerSecrets],
},
},
});
return {
overlays: [...externalFetch.overlays],
// ...
};Harness-v5 stores optional strict group identity on each flat variant so complexity checks count conceptual groups while retaining per-variant coverage and trace recall. Ordinary overlays remain unchanged. One-variant groups, groups above six variants, more than 32 flat overlays, duplicate effective context, and variants with no context delta fail or warn through the normal complexity gate.
const applicationSources = h.sourceSet.define({
id: "application-sources",
patterns: ["app/**", "src/**", "lib/**"],
});
const implementation = h.route.define({
id: "implementation",
triggers: { paths: [applicationSources, "packages/**"] },
});
const source = h.fileClass.define({
id: "source",
patterns: [applicationSources, "tests/**"],
recommendations: [],
});
const fallback = h.route.fallback({
id: "fallback",
intentionalPaths: [applicationSources, "README.md"],
});h.sourceSet.harnessInputs(options?) is a separate dynamic handle accepted only beside literal file-class patterns. The compiler expands it to harness-v5 string patterns.
Its closure comes from the prepared config graph, referenced knowledge, explicit required files, the fixed artifact pair, configured generated surfaces, and selected human-owned root adapters. Runtime traces are excluded unless runtime-evidence is explicitly included.
Referenced knowledge includes each reference file and its declared authority files. Authority bytes also participate in the discovery fingerprint even when the routed reference itself is unchanged.
include selects facets and exclude removes facets, so references can remain under a different file-class gate.
const harnessInputs = h.fileClass.define({
id: "harness-inputs",
patterns: [h.sourceSet.harnessInputs({ exclude: ["references"] }), "docs/harness-policy.md"],
recommendations: h.command.recommend({
command: checkHarness,
reason: "Harness inputs must leave generated surfaces fresh",
}),
});Split modules can contribute definitions without making the root re-list every handle. h.scope(id, scoped => ({ exports, routes, overlays, evals, fileClasses, subagents })) requires explicit exports and contributions. Grouped variants contribute and export their ordinary overlay handles.
h.compose({ core, fallback, scopes, ... }) flattens them. A dependent scope calls scoped.imports(otherScope.exports) before using public handles. Omitted scopes, private imports, uncontributed definitions or exports, duplicate contributions, and foreign owners fail closed.
Existing configs may continue returning HarnessAuthoringResult directly.
const routing = h.scope("routing", (scoped) => {
const fallback = scoped.route.fallback({ id: "fallback" });
return { exports: { fallback }, routes: [fallback] };
});
const evals = h.scope("evals", (scoped) => {
const publicRouting = scoped.imports(routing.exports);
const fallback = scoped.eval.case({
id: "fallback-general",
prompt: "Explain the repository",
expect: { route: publicRouting.fallback },
});
return { exports: {}, evals: [fallback] };
});
return h.compose({
core: { mandatory: [] },
fallback: routing.exports.fallback,
scopes: [routing, evals],
});Registry keys are local aliases, eval-map keys are explicit eval IDs, and reasons and artifact IDs remain explicit. Scalar and batch declarations with the same decoded meaning may have different config fingerprints because fingerprints include the exact checked source graph.
Route evals make context promotion intent explicit. When a selected overlay supplies route-conditional context as mandatory, authoring fails with eval.promotions_required unless expect.promotions names the promoted context in the exact composed order.
A supplied list that differs from the composed result fails with eval.promotions_mismatch; when there are no promotions, omission or an empty list is valid.
const testing = h.context.file("docs/testing.md", "Use test policy");
const implementation = h.route.define({
id: "implementation",
triggers: { intents: ["implement"] },
conditional: [testing],
});
const verify = h.route.overlay({
id: "verify",
triggers: { keywords: ["verify"] },
mandatory: [testing],
});
const implementationEval = h.eval.case({
id: "implementation-verify",
prompt: "Implement and verify the change",
expect: { route: implementation, overlays: [verify], promotions: [testing] },
});The authoring API and runtime client deliberately use separate entry points. 1000skills/harness stays dependency-light and hermetic because it is the only module a harness config may import.
1000skills/harness/client is for programs that consume versioned CLI envelopes and therefore includes runtime schema validation. Keeping that boundary prevents authoring configs from loading the runtime validator while allowing both interfaces to evolve within the same package.
The config graph is closed and bounded to 8 import levels, 32 files including the root, and 512 KiB of source. Static imports may target only 1000skills/harness or explicit relative .ts files whose logical and real paths remain under .agents/harness/.
Aliases, other bare packages, JavaScript, JSON, extensionless imports, dynamic imports, cycles, duplicate realpaths, symlink escapes, and oversized graphs are rejected.
Sync typechecks every source module before execution with SDK-pinned TypeScript 6.0.3 and hardcoded strict options. It does not load repository tsconfig files, plugins, path aliases, ambient project declarations, or suppression directives.
as const and satisfies are supported; explicit any, unsafe assertions, non-null assertions, and TypeScript suppression/reference directives are rejected.
The parent process securely reads, validates, typechecks, and transpiles one bounded source snapshot. Two fresh sanitized VM children receive those exact immutable bytes over stdin; they never reread target config files.
harness.config.ts and the exact imported .agents/harness/*.ts graph are human-owned source. .agents/harness.json, .agents/harness.evals.json, and configured generated adapter surfaces are generated. Root adapter entrypoints such as AGENTS.md and CLAUDE.md remain human-owned.
Source-set closure diagnostics retain this ownership distinction even though artifacts contain only literal paths.
Use h.eval.routeCases when several prompts share one route expectation and h.eval.changedCases when several paths share one command set. Route array/map cases inherit shared overlays and promotions; an explicit per-case array, including [], deterministically replaces the shared value.
h.command.packageScript verifies that a declared script exists in the selected manifest while still emitting fixed argv. h.command.define, h.command.packageScript, and package-script registry entries accept same-owner command handles in subsumes.
Direct targets are deduplicated deterministically and emitted as artifact-only command IDs. A selected command suppresses a direct or transitive target only when that target is also selected; the relation never expands a recommendation set.
Commands
1000skills harness init
1000skills harness sync
1000skills harness sync --check
1000skills harness sync --diff
1000skills harness sync --repair
1000skills harness sync --accept-authority
1000skills harness check
1000skills harness check --baseline origin/main
1000skills harness check --exhaustive
1000skills harness route "Fix the failing selector" --explain
1000skills harness changed -- src/button.ts
1000skills harness changed --require-classification -- src/button.ts
1000skills harness changed --from origin/main
1000skills harness changed --from origin/main --to HEAD
1000skills harness inspect --prompt "Fix the failing selector"
1000skills harness inspect --file src/button.ts
1000skills harness inspect --id bug-fix
1000skills harness inspect --target claude
1000skills harness suggest-evals
1000skills harness trace start --session repair-1 --route bug-fix
1000skills harness trace record --session repair-1 --path AGENTS.md
1000skills harness trace finish --session repair-1
1000skills harness diagnoseAdd --json to any command for output-envelope version 5. Current trace writers use protocol version 2. The CLI allows stdout and stderr to drain before the process exits, including JSON envelopes larger than the platform pipe buffer.
Runtime, check, diff, diagnose, and baseline commands load only harness-v5 .agents/harness.json and eval-v5 .agents/harness.evals.json, never execute TypeScript, and fail with artifact.schema_version_unsupported for unsupported schema versions.
They fail closed on malformed, incomplete, unowned, or mismatched artifacts. Sync owns atomic paired generation and interrupted-transaction recovery.
For ordinary drift, edit harness.config.ts or human-owned knowledge and run normal sync.
Authority drift is intentionally different from ordinary generated drift. check and diagnose emit authority.review_required with the affected reference and authority path. Normal sync refuses to reset the recorded fingerprint.
After semantic review, sync --accept-authority accepts the current secure authority files in write mode. The flag is invalid with --check or --diff.
When Claude is a tool target, sync also tracks its managed UserPromptSubmit activation in .claude/settings.json. Check and diff modes report a missing or changed managed hook without writing. Normal sync repairs only that managed entry and preserves unrelated settings and hooks.
The generated hook selects the first configured Node candidate satisfying the package's >=22.12 engine range. The generated hook routes with --format hook but never adds --trace.
Start traces through explicit trace start/record/finish commands, or explicitly pass --format hook --trace when a caller owns the complete trace lifecycle.
Managed surface writes use a fixed recovery record plus deterministic sibling stage and recovery paths.
A later write sync restores or finishes an interrupted per-file mutation before compilation, including interrupted .claude/settings.json merges; check and diff modes fail with generated.recovery_required instead of changing recovery state.
Custom and h.subagent.readOnly adapters are generated for Claude (.claude/agents/*.md), OpenCode (.opencode/agents/*.md), Cursor (.cursor/agents/*.md), Codex (.codex/agents/*.toml), and Pi.
Pi keeps definitions in .agents/harness.json and generates only .pi/extensions/1000skills/index.ts; it does not duplicate definitions under .pi/agents.
Read-only agents use each target's native restriction: a Claude tool allowlist, OpenCode edit and bash denials, Cursor readonly: true, Codex sandbox_mode = "read-only", or Pi's read, grep, find, and ls tool allowlist.
Custom agents omit restriction claims where a target cannot map arbitrary tool names.
Prompt routing uses a Claude UserPromptSubmit hook, a Cursor always-applied rule, and the canonical AGENTS.md instruction consumed natively by OpenCode, Codex, and Pi.
With bridgeSkills: true, sync creates Claude skill bridges for both explicit h.reference.skill entries and skills resolved through h.discover.skills; Cursor, OpenCode, Codex, and Pi use native .agents/skills discovery without duplicate links.
Adding a supported agent
Supported agents use one closed, compile-time registry.
Add the target ID and artifact policy in src/contracts/tool-targets.ts, add one HarnessAdapter module under src/compiler/agent-adapters/, and register it in src/compiler/agent-adapters/registry.ts.
If goal-loop can launch the agent, add one goal-loop adapter module and register it in src/goal-loop/adapters/registry.ts.
Do not add target checks to schema, CLI, source-set, diagnostics, or generated-surface code. These consumers derive target IDs, instruction inputs, managed directories, generated prefixes, delivery capabilities, and executable settings from the registries. Add the target to the cross-tool fixture and registry conformance tests, then regenerate schemas and API reports.
Use sync --repair only after diagnosis confirms that generated artifacts are malformed; it stages and validates a complete replacement pair before touching either fixed target. Do not use repair instead of correcting typed source.
The top-level packageManager result field defaults to npm and accepts npm, pnpm, or yarn. It controls generated package-aware adapter commands and is the default for h.command.packageScript; a package-script declaration may override it for a different manifest.
Package scripts compile to npm run <script>, pnpm run <script>, or yarn <script> argv.
Generated Cursor routing rules use the exact local-binary wrappers below, and diagnostics parse the same forms rather than accepting lookalikes:
| Package manager | Generated route command |
| --------------- | ----------------------------------------------- |
| npm | npm exec -- 1000skills harness route "<task>" |
| pnpm | pnpm exec 1000skills harness route "<task>" |
| Yarn | yarn 1000skills harness route "<task>" |
Changed-file recommendations return structured argv and never execute commands. Results always include unmatchedFiles and a deterministic effective plan that lists each retained command once with its contributing file classes, files, reasons, cwd, and argv.
Raw groups remain explanatory, while retained commands inherit provenance from selected commands they transitively suppress. Artifact-only subsumes metadata is not included in output-v5 changed command objects.
--require-classification returns exit 5 with changed.files_unclassified while preserving the complete evidence payload. Use inspect --file <path> for one-path classification and plan explanations; changed does not overload the file separator with an explain mode.
A matched file class remains an explicit output group even when it intentionally recommends no commands, so metadata-only classes can return commands: [] and reasons: [].
Initialized check:changed package scripts end in --, allowing npm run check:changed -- path and equivalent package-manager invocations to forward file paths naturally.
Route prompt path signals accept repository-relative, ./, <repo-root>, POSIX absolute, and Windows absolute forms. CLI routing passes the actual repository root so contained absolute paths become relative trigger candidates; paths outside that root remain absolute and are not falsely stripped.
Path extraction recognizes Next.js dynamic ([id]), catch-all ([...slug]), optional catch-all ([[...slug]]), and route-group ((marketing)) segments.
It also recognizes the case-sensitive conventional root files Dockerfile, LICENSE, Makefile, and Procfile, while ordinary parenthesized or lowercase prose is not treated as a path. Candidates matching a declared route or overlay path trigger remain path-only.
Unmatched slash-separated candidates contribute normalized segment words back to keyword and pattern matching, so prose such as privacy/logging/provider is not discarded.
inspect accepts exactly one of --prompt, --file, --id, or --target. Prompt inspection reports the selected route and competitors, exact selected-overlay signals, context suppliers, deduplication, conditional-to-mandatory promotions, and per-overlay net deltas.
File inspection reports normalization, every raw matching class and recommendation, reasons, and the collapsed effective command plan. Definition inspection reports derivable relations and eval coverage; command definitions include direct subsumes and subsumed-by edges.
Target inspection reads the generated repository artifact, reports whether adapter metadata is available, and distinguishes native restrictions created by h.subagent.readOnly from custom-agent prose and tool policy.
Artifacts generated before adapter metadata was added report configuration and restriction evidence as unknown; custom agents are never reported as natively read-only.
Coverage checks are built from actual routing and changed-file classification results. A recommendation is covered when an eval retains that command or a selected command that transitively subsumes it; changed eval expectations compare effective recommendation IDs in historical recommendation order.
Normal check and sync require behavioral evidence for every route, overlay, file class, fallback, recommendation, and declared trigger kind. They do not require one eval per synonym or literal file pattern.
check --exhaustive adds every actionable diagnose finding to the quality gate, promotes diagnostic warnings to errors, and exits 5 when any remain.
diagnose and suggest-evals retain exhaustive per-signal and per-pattern evidence for audits without treating every alias as an ordinary quality-gate warning.
Overlay coverage requires global positive and negative evidence; the SDK does not invent obligations for every undeclared route-overlay cross-product.
The shared graph records eval IDs per route and overlay signal, confusion edges, observed overlay-route evidence, file-class patterns and intersections, and recommendation coverage.
When secure inventory matches uncovered file-class patterns, suggest-evals groups paths with the same effective command plan. It emits the exact recommendation union across every matching class.
Signal suggestions exercise the exact uncovered keyword, intent, or path; when no concrete evidence can exercise the gap, the result returns a blocker instead of substituting another owner signal or fabricating a path.
Unsupported route and overlay patterns fail with the owner, offending pattern, and exact bounded-subset constraint rather than a generic regular-expression error.
Diagnostic details summarize large evidence lists as deterministic samples with total and omitted counts, preserving owner, signal, and aggregate issue evidence without returning unbounded inventory arrays.
diagnose --json returns required actionable issues, required non-actionable observations, clean, and bounded metrics without changing its exit-zero evidence behavior.
clean is true exactly when actionable issues are empty; observations remain visible audit evidence and never act as warnings.
metrics.trackedFiles reports whether Git tracked-file evidence was available and the tracked, classified, unclassified, synthetic-fallback, intentional-fallback, and unexplained-fallback counts. syntheticFallback always equals intentionalFallback + unexplainedFallback.
metrics.routeEvalMandatoryContext.words and .units report count, nearest-rank p50, nearest-rank p95, and max across route-eval mandatory context.
Direct files are securely read and counted; unmeasured reports route evals whose direct-file words were unavailable instead of adding zero-valued word samples.
Tracked-file diagnostics use actionable diagnose.tracked_file_unclassified issues. Intentional fallback matches produce observational diagnose.tracked_file_fallback evidence; fallback wins outside intentionalPaths produce actionable diagnose.tracked_file_fallback_unexplained issues.
Explicitly eval-declared promotions, same-context-ID duplicate suppliers, equivalent same-ID placements, promotion-intended observed same-ID mandatory/conditional status differences, and theoretical same-ID mandatory/conditional overlaps without not-selected context are observations.
Different IDs resolving to one target even theoretically, reason conflicts, every status conflict involving not-selected context, undeclared observed mandatory/conditional conflicts, and zero-delta overlays remain issues.
Trace diagnostics include malformed or inconsistent logs, invalid session IDs, and diagnose.trace_unfinished for sessions idle more than 24 hours.
Finished trace symptoms are clustered by route, symptom, and explicit cause category: mandatory-missing causes are route-stale, trigger-too-broad, agent-skipped, file-missing, or unknown; not-selected violations are over-read, not-selected-wrong, or unknown.
All path, session, item, and eval-ID evidence uses bounded deterministic samples.
Exit codes are stable:
| Code | Meaning | | ---: | --------------------------------------- | | 0 | Success | | 2 | CLI misuse | | 3 | Invalid configuration or security state | | 4 | Generated artifact drift | | 5 | Eval or quality failure | | 70 | Internal failure |
Generated contracts
sync writes current harness-v5 .agents/harness.json and eval-v5 .agents/harness.evals.json as one rollback-capable transaction.
Harness core, route, and overlay context entries use a content-addressed contexts registry; route eval cases use an eval-local expectations registry whose expectations include ordered promotions.
Registry IDs use 64-bit base64url digests (cx- or ep- plus 11 characters), context targets use tagged file/reference tuples, and route/overlay trigger objects omit empty categories.
Strict parsers validate registry references, IDs, command subsumption targets, self edges, and cycles, then hydrate both wire artifacts into expanded private runtime models before routing, changed-file checks, diagnostics, or eval execution.
Matching generator, config, and discovery fingerprints bind the pair. The config fingerprint covers both decoded authoring semantics and the normalized source graph, so source-only edits intentionally produce artifact drift.
Artifacts contain no timestamps, absolute paths, environment data, or secrets.
The package publishes Draft 2020-12 schemas:
1000skills/schema/harness.json(current v5)1000skills/schema/harness-evals.json(current v5)1000skills/schema/command-envelope.json(current v5)1000skills/schema/trace-event.json(current v2)
Runtime, check, diff, diagnose, and baseline readers accept only this current harness-v5/eval-v5 pair. They do not migrate old artifacts and report artifact.schema_version_unsupported when the pair is not current.
Normal write-mode sync also refuses stale or unsupported schema pairs; it never migrates them. Malformed, mismatched, mixed-owner, and unowned pairs continue to fail closed.
sync --repair is the only intentional path that may replace a refused pair with artifacts compiled from current typed source.
JSON-only repositories may author current harness-v5 and eval-v5 artifacts directly and use the same runtime commands.
Client parsers
Client parsers use a separate entrypoint because they serve a different caller. 1000skills/harness stays a minimal hermetic authoring interface for harness.config.ts; 1000skills/harness/client is the runtime-facing interface for programs that consume current CLI envelopes.
import { assertOk, parseRouteEnvelope } from "1000skills/harness/client";
export function readRoute(cliStdout: string): string {
const envelope = parseRouteEnvelope(cliStdout);
assertOk(envelope);
return envelope.data.routeId;
}Parsers validate JSON, the envelope, output version, command identity, and command-specific data. They accept only current output v5; output versions 2, 3, and 4 are unsupported and fail with client.output_version_unsupported.
Output-v5 diagnose requires observations plus intentionalFallback and unexplainedFallback, and enforces clean === (issues.length === 0). Trace envelopes also use output v5 while trace events retain the current trace protocol v2 contract.
CommandEnvelope is a discriminated union on ok: a valid ok: false envelope (FailedEnvelope) can be inspected manually via if (!envelope.ok), or assertOk narrows to SuccessfulEnvelope and throws on failure.
Development
Run from the repository root:
npm install
npm run typecheck
npm test
npm run buildTypeScript 7.0.2 is the package build compiler and emits JavaScript and declarations directly. The closed harness-config checker is pinned independently to TypeScript 6.0.3, and the packed-consumer gate compiles declaration fixtures with both versions. The package also uses Vitest 4, TypeBox, Ajv, Changesets, publint, Are the Types Wrong, API Extractor, Knip, and JSCPD; direct dependencies are pinned exactly.
npm run api:update --workspace=1000skills is the only command that updates checked-in API reports. npm run api:check --workspace=1000skills compares declarations and normalized reports without writing them.
prepack runs package verification, but it does not run the complete exact-tarball consumer gate. release:candidate owns the full workflow: archive the exact committed source, install its lockfile with lifecycle scripts disabled, run the controlled verification/build commands, and pack once.
It checks that exact tarball with publint, Are the Types Wrong, npm, Yarn, pnpm, TypeScript 6.0.3, and TypeScript 7.0.2.
Release
npm run release:candidate
git add docs/releases/candidates/1000skills/<version>.json
gh workflow run publish.yml --ref mainAfter package documentation is approved, run npm run release:candidate. It requires clean committed source and writes one sealed tarball under /tmp/1000skills-release/1000skills/<version>/<candidate-id>/; macOS may expose the same directory through /private/tmp.
Review and commit the path-free durable receipt at docs/releases/candidates/1000skills/<version>.json before authorizing publication.
The receipt binds the package and version to an opaque candidate ID, source revision, root tree, and a canonical SHA-256 over the sorted Git blob modes, paths, and bytes in the package source plus every repository-owned verification input declared by the release builder.
It also records the artifact SHA-256, npm SHA-1, and npm SRI. A metadata-only later commit may publish it only while the current package-source digest remains identical. The staging hierarchy must be owned by the current user, use mode 0700, and contain no symlinks.
The sealed candidate directory and tarball use modes 0500 and 0400; another build cannot replace either one.
The protected publish.yml workflow reconstructs the exact receipt-bound artifact once, uploads/downloads those bytes between jobs, and publishes with GitHub OIDC plus npm provenance. It never repacks in the publish job.
Manual npm run release is a fail-closed fallback that requires an explicit RELEASE_NPM_IDENTITY, a GitHub token authorized to create the durable lease, exact npm config and identity preflight, and authoritative version absence.
Publication uses explicit public-registry, latest, public-access, and no-lifecycle-script flags. An atomic protected GitHub ref keyed by version and candidate prevents every second invocation, including fresh-runner retries.
A successful publish runs the same registry verifier exposed as npm run release:verify; every failure after lease creation is resolved through recover-release.yml or the verifier instead of another publish attempt. Recovery needs only the durable receipt and registry bytes, not local staging.
Temporary consumers, rather than the local link, prove the packed artifact before publication.
