SuperAPI AI Directory

gate-customization

Deployment & operations

View repository

Updated 10-15 21:40

About this skill

Gate customization (settings.gates completion gates)

This skill teaches you to declare task completion gates in the target repository's harness.yaml : attach local tests, set manual sign-off checkpoints, configure dual control and special approval, and exempt built-in gates. All fields and behaviors are governed by the current source code—the contract is in packages/kernel/src/domain/completion-contract.ts, YAML reading/writing and validation are in packages/kernel/src/domain/settings.ts, per-gate determination is in packages/kernel/src/domain/closeout-readiness.ts(judgeGateWitnesses), adapter execution and manual witness admission are in packages/daemon/src/repo-cell-witness-adapters.ts. Before changing configuration, read the parts of these files you need; do not write from memory.

Model: Declaration × Mapping × Freezing

A completion gate is assembled from three layers; if any layer is missing, it does not work:

Layer Who declares Where
Task declares gate id The profile of the preset used by the task completionGates Preset package (built-in see packages/preset/assets/software-coding/presets/*/preset.json)
Repository maps witness settings.gates.<gateId> → adapter This repository harness.yaml
Freeze at submission submit freezes the resolution result together with adapterOptions into completionContract travels with the submitted cut; changing YAML does not affect a cut under review

Decision rules (fail-closed):

  • The task declares a gate but settings.gates has no mapping for it → submit reports gate_mapping_invalid, not a silent skip.
  • Mapped to none → the requirement is removed; no false pass is minted.
  • The repository maps a gate id that no task declares → legal but inert; it produces no requirements.
  • Unknown adapter, unknown field, imprecise field set → error upon reading settings.

The built-in preset declares only two gate ids:ci and code-doc-reconciliation. To use a custom id (such as lint、signoff、merged-to.main), the task side must have a preset that declares it: package it according to preset-creator (write the custom id into the profile's completionGates ), install it withha preset install --source <包目录> , and use it withha task create --preset <id> . Changing only settings.gates without changing the preset means the custom gate will not be required by any task.

Two compile-time elimination rules (effective at task creation, before any mapping):

  • settings.ci.workflows is an empty array (or the entire ci: section is missing) → preset resolution removes ci from the task's completionGateIds (atpackages/preset/src/preset-runtime.ts profile.completionGateIds). If a repository without GitHub CI wants tests witnessed locally, use a custom gate id with a preset; do not rely on a ci mapping.
  • workKind: docs task → both ci and code-doc-reconciliation are eliminated (packages/preset/src/preset-bootstrap.ts withoutCodeDeliveryGates)。

settings.gates syntax

Write under harness.yaml of settings: . Indentation-sensitive:gates: two spaces, gate id four spaces, fields six spaces; field line order is arbitrary; values may be followed by comments.

settings:
  ci:
    workflows: [rewrite-ci]        # github-actions 适配器共用的 workflow 注册表
  gates:
    ci:                            # 块形式:下面六空格字段
      adapter: github-actions
      appliesTo: code
      branch: main
      event: push
      coverage: descendant
      selection: newest
    lint:                          # 自定义 id(需 preset 声明)
      appliesTo: code
      adapter: local-command
      command: npm ci && npm run lint
    signoff:
      appliesTo: submission
      adapter: manual-attest
    code-doc-reconciliation: none  # none 只能内联,后面不许再跟字段

Parsing rules (settings.ts readGateSettings/gateSettingsSchema/gateWitnessMappingIssues):

  • gate id pattern ^[A-Za-z0-9][A-Za-z0-9/_.@-]*$: no spaces or colons. Write a merged gate as merged-to.main, not merged-to:main。
  • Inline values may only be none;ci: github-actions ; this kind of inline form directly reports settings.gates cannot read line。
  • Each adapter's field set isexactyes; one more or one fewer is an error must declare exactly:
adapter Required fields (exactly these) Optional governance fields
github-actions appliesTo branch event coverage selection mandatorySignoff allowOverride
local-command appliesTo command mandatorySignoff allowOverride
manual-attest appliesTo None (see below)
none None (can only be inline) None
  • mandatorySignoff / allowOverride is a governance modifier field and is not counted in the exact set above: it may only be written on github-actions and local-command , and writing it on manual-attest or none will report cannot declare mandatorySignoff or allowOverride(gates with a human witness no longer need these two modifiers). The value must be a YAML boolean true/false; other forms (such as yes) are rejected by the schema; only true enters the frozen contract, and writing false is equivalent to not writing it.

  • github-actions 's workflow list is not written in the gate mapping; it uniformly comes from settings.ci.workflows(inline array [a, b], names without .yml); mapping github-actions while workflows is empty → parse error.

  • branch/event values are restricted by ^[A-Za-z0-9][A-Za-z0-9/_.@-]*$ ;coverage recognizes only exact(the run's head SHA must be exactly the commit SHA) or descendant(the run's head SHA has the commit SHA as an ancestor);selection recognizes only newest。

  • duplicate fields within the same gate, six-space fields appearing before the gate line, unknown lines → cannot read line。

  • # acts as a comment on any line and is stripped—the command value cannot contain #(command: echo a#b will become echo a)。

  • code-doc-reconciliation is a Harness internal checker and can only be mapped to none; mapping any adapter reports an error.

  • gates is a repository-owned setting;ha settings update 's input fields do not include gates; manually editing harness.yaml is the official channel, and rewriting other settings will not touch the gates section. After editing, use ha settings show to read back and verify.

appliesTo selection

There are only three enum values (completion-contract.ts gateAppliesTo), which take effect according to the shape of the commit cut:

Value When it applies Typical use
submission Applies to every commit cut Manual sign-off, checks on the deliverable itself
code cut has a delivery commit (commitSha non-empty) Tests, lint, build, CI, merge gate
artifacts cut has at least one accepted artifact anchor Artifact sign-off, report review

Verdict semantics: a gate that does not apply is recorded on that cut as not_applicable, does not block, and requires no evidence. A mixed delivery (commit + artifact) carries both kinds of gates at the same time.

Practical rules:

  • local-command requires a commit cut (it needs to materialize the commit into a sandbox); on an artifact-only delivery it will report witness_unavailable. For pure documentation/artifact deliveries use manual-attest。
  • When a repository has both code tasks and documentation tasks, scope the code gates as appliesTo: code, so documentation tasks will not be blocked; use delivery-level checkpoints (such as owner sign-off) as submission。

Scenario: local-command (local command witness)

Treat a shell command as the witness for a gate. Execution semantics (repo-cell-witness-adapters.ts collectLocalCommand):

  • Trigger timing:task-submit and task-complete before the write queue; it is recollected every time until the gate already has an accepted pass witness bound to the current cut (or has been specially approved by the owner as waived — waived gates are no longer recollected). The previous fail is not frozen as final — after fixing, rerunning task complete will collect a new pass.
  • Sandbox: the daemon usesthe exact commitwith git archive to unpack into a brand-new temporary directory and then run the command. What the command sees is the content at commit time, not the working tree; in the directory there isno .git. The temporary directory is deleted after use, and no state is retained between two runs (whatever dependencies were installed will not remain).
  • Command form:sh -c "<command> 2>&1", stdout and stderr are merged.
  • Environment variables: only PATH plus the following three; all other environment from the parent process (HOME, node/npm configuration, etc.) is not passed at all:
Variable Value
HARNESS_WITNESS_CUT SHA of the witnessed commit
HARNESS_WITNESS_GATE gate id
HARNESS_WITNESS_REPO source repository root (containing .git), used for repository-level observation (such as merge ancestor checks)
  • The exit code is the verdict:0 = pass; nonzero = fail (at complete, blocking completion with invalid_proof ; when the gate declares allowOverride , a fail is first recorded as an official receipt, and the blocker is changed to the special approval command); startup failure, being killed by a signal, or exceeding 10 minutes = witness_unavailable(evidence unavailable, neither pass nor fail; the task remains stuck on gate unmet).
  • Staleness prevention: the observation is bound to commitSha + submissionDigest; after the commit is amended or the contract changes, the old observation is invalidated and must be recollected.
  • Reachability: the commit must exist in a repository readable by this node (the repository root or the worktree dispatched for this task). If a worker commits in a worktree but does not make it readable by the daemon, it will report witness_unavailable。
  • Trace retention: provenance records exit N; output sha256:…; tail "…"(output summary + the last about 500 characters); the full output is not stored.

Rules for writing commands:

  1. Bring your own dependency installation (the sandbox is new every time):npm ci && npm test、python3 -m venv .venv && …。
  2. Do not rely on HOME-type caches. Measured in a minimal environment: Go must explicitly export GOCACHE="$PWD/.gocache", otherwise it reports GOCACHE is not defined and $HOME is not defined; Rust is advised to explicitly export CARGO_HOME="$PWD/.cargo-home"(when there are dependencies, the registry also goes through it).
  3. Checks that need to read git history (merge gates, tag checks) should use git -C "$HARNESS_WITNESS_REPO"; do not expect the working directory to contain .git。
  4. The timeout is 10 minutes: when full installation + full testing does not fit in one command, split it into multiple gates or have the command create its own cache directory.

Merge gate example(the merged-to form of decision dec_59FA45A407F850E2B167A192D7, effective after declaration with a custom gate id and preset):

  gates:
    merged-to.main:
      appliesTo: code
      adapter: local-command
      command: git -C "$HARNESS_WITNESS_REPO" merge-base --is-ancestor "$HARNESS_WITNESS_CUT" refs/heads/main

Behavior: at commit time main does not yet include this cut → a fail is collected (does not block submit; the verdict is judged at complete); after merging into main, rerun task complete → pass。

Scenario: manual-attest (manual sign-off checkpoint)

The adapter itself collects no observation: the witness is written by a human through the CLI.

ha task attest <task-id> --gate <gate-id> --result pass --note "审阅意见"
  • --result only recognizes pass or fail;--note is optional and will go into the evidence's rawResult;--mode defaults to approve。
  • Preconditions (reject if any is missing): the task's current iteration has a committed execution; the gate is inFrozen contractin; the adapter declared by this gate is manual-attest(for a gate declared as github-actions with no mandatorySignoff , attesting will be rejected with invalid_command - a manual pass cannot substitute for the declared witness source);appliesTo matches this cut (a code gate cannot sign off on an artifact-only cut).
  • The evidence provenance is source: human, bound to the execution identity at that time. A runtime session with an executor can never sign off as a manual identity (actor_unauthorized) - only a human principal is recognized for sign-off. It does not replace the Review/Consent process, it is only a gate witness.

Suitable for: sign-off checkpoints in environments without automation, manual review of artifact-only deliveries, owner approval before go-live.

Scenario: mandatorySignoff (automatic witness + mandatory manual sign-off, dual control)

Add to the github-actions or local-command gate: mandatorySignoff: true:

  gates:
    ci:
      adapter: github-actions
      appliesTo: code
      branch: main
      event: push
      coverage: descendant
      selection: newest
      mandatorySignoff: true

Behavior (judgeGateWitnesses): after the automatic witness passes, the gate status is signoff_missing rather than passed- the blocker for complete directly provides the sign-off command:

ha task attest <task-id> --gate ci --result pass
  • Sign-off goes through the same task attest entry point,--mode defaults to approve; it requires that this cut already hasan accepted automatic pass, otherwise it rejects invalid_transition(run ha task complete first to persist the witness). approve cannot carry --rationale(that is an override field,invalid_field), available --note。
  • Any human principal can sign off (not limited to the owner); a runtime executor cannot.
  • Sign-off and the automatic witness are two independent evidence slots (lanes): a human sign-off does not overwrite the machine record, and machine re-collection does not overwrite the human sign-off.
  • manual-attest and none gates cannot declare this field.

Scenario: allowOverride (owner break-glass special approval)

Add to the github-actions or local-command gate: allowOverride: true, leaving the task owner an emergency channel of "a recorded failure can be exempted":

  gates:
    e2e:
      adapter: local-command
      appliesTo: code
      command: npm run e2e
      allowOverride: true

Special approval command (provided by the blocker for complete and by the rejection message when collection fails):

ha task attest <task-id> --gate e2e --result pass --mode override --rationale "<为什么豁免,trim 后至少 10 字符>"

Admission and semantics (attestGateWitness / judgeGateWitnesses, verified item by item against the source code):

  • The gate's frozen contract must have declared allowOverride- adding it to YAML after submission does not affect the cut under review (invalid_command)。
  • Only the task owner (the principal corresponding tocreatedBy ) can grant special approval; others actor_unauthorized; a runtime executor cannot grant special approval.
  • Two exemption shapes:there is a recorded automatic fail -> the special approval is bound precisely to that receipt (recorded in the evidence as waivedReceiptId: <receipt id>);this cut has no automatic receipt at all(runner unreachable, collection timeout, etc., no witness ever produced) -> waivedReceiptId explicitly records null, meaning "exempt the absent automatic witness". When this cut already has an automatic pass or other witness, override rejects invalid_transition- a real pass does not need special approval.
  • Any machine receipt that arrives later invalidates the old special approval: new fail -> the gate returns to failed, and the new receipt must be specially approved again; new pass -> passed(if the gate is also mandatorySignoff, it returns to signoff_missing, requiring an independent approve). A gate that has been waived is no longer re-collected (local-command is not re-run, CI is not re-pulled), but already recorded observations are re-judged as usual, and new receipts are persisted as usual.
  • override never counts as signoff: a manual witness with override does not participate in the mandatorySignoff determination; but a gate that has been specially approved is recorded on the whole as waived(satisfied state, not shown as passed), and no longer needs a separate sign-off.
  • Special approval is also bound to commitSha + submissionDigest: after the cut is amended or the contract changes, evidence must be re-collected.
  • allowOverride A gate's automatic fail will first be persisted as a formal receipt (leaving the owner an exemptible receipt), and then complete's blocker gives the special-approval command; when collection itself fails (gh unreachable, command timeout), the rejection message likewise includes an override hint.

GUI corresponding entry: the daemon registers task.attest GUI action(repo.task.attest / bridge method taskAttest / POST /api/tasks/:taskId/attest), payload is the closed shape { taskId, gateId, result, mode?, note?, rationale? }—the override's rationale must go in rationale(putting it in note will be rejected for missing rationale); the renderer button's show/hide rules (only failed/missing and frozen-contract allowOverride gates show special approval,waived is displayed as specially approved release rather than passed) are implemented on the GUI side.

Scenario: none (explicit exemption)

  gates:
    ci: none
    code-doc-reconciliation: none

none removes that gate requirement declared by the task: nothing runs, no pass is minted, and it no longer appears in the completion determination. It is an explicit declaration—"no mapping written" and "none written" are two different things: the former is a configuration error for an already-declared gate, the latter is the repository's ruling.code-doc-reconciliation can only be removed with none .

Common combinations: an internal tools repo doesn't want to be blocked by code-doc reconciliation → code-doc-reconciliation: none; a repo without GitHub CI → ci.workflows: [](the preset side will simply not declare cito the task, no need to write none).

Scenario: github-actions (the default witness for the built-in ci gate)

ci is the only built-in declared gate and by default goes through GitHub Actions. See the syntax section above for the mapping syntax; key points:

  • The workflow list always comes from settings.ci.workflows, and the mapping has no workflows field.
  • coverage: descendant accepts subsequent runs with the commit SHA as ancestor (the scenario where CI runs on the merge commit after a merge PR is submitted);exact requires the run to be attached to the commit SHA.
  • selection: newest is currently the only selection strategy (highest run/attempt preferred).
  • This repo's (harness-anything itself) baseline mapping is ci → github-actions + rewrite-ci, for comparison.

Multi-language examples

examples/ below is the fully parseable harness.yaml(all pass readSettingsFacet validation; command recipes tested in a minimal PATH-only environment):

File Scenario
examples/harness-node.yaml Node repo: GitHub CI witness ci + local command witness lint + merge gate
examples/harness-python.yaml Python repo: no GitHub CI, tests all via local command witness
examples/harness-go.yaml Go repo: local vet+test (explicit GOCACHE)
examples/harness-rust.yaml Rust repo: local cargo test --locked (explicit CARGO_HOME)
examples/harness-manual-only.yaml No automation environment: exempt built-in gates + manual signature checkpoint
examples/harness-docs-only.yaml Pure docs repo: artifact delivery + manual review signature
examples/harness-governance.yaml Governance fields: CI dual-control signoff (mandatorySignoff) + e2e special approval (allowOverride)

Except for the Node example's ci , the custom gate ids in the examples all need a matching preset declaration to be required by the task (see the "Model" section).

Troubleshooting comparison

Symptom Meaning Handling
Task declares completion gate X, but harness.yaml settings.gates maps no witness for it The task declares a gate, but the repository has no mapping Add the mapping or write X: none
settings.gates.X with adapter Y must declare exactly: … The field set is imprecise Complete or remove extra fields according to the field table
settings.gates cannot read line: … Indentation error, inline value not none, duplicate field Fix the YAML against the syntax section
Gate X is witnessed by github-actions, but settings.ci.workflows is empty The workflows registry is empty Fill in settings.ci.workflows or switch adapters
witness_unavailable(local-command) Commit unreachable / artifact-only cut / command timeout or startup failure Confirm the commit is in a repository readable by the daemon; for artifact delivery switch to manual-attest; check whether the command is stuck;allowOverride Under the gate, special approval may be granted as prompted
Gate X receipt … reported fail(invalid_proof, when complete) The command exits nonzero or the witness observation is fail (the gate did not declare allowOverride) Fix it to exit code 0 (or after merge/attest), then rerun task complete
gate status signoff_missing The automatic witness has passed, but the mandatorySignoff gate lacks a human signoff ha task attest <task> --gate <g> --result pass
gate status waived The owner has granted special approval (satisfied state, not passed) No action needed; note that a new automatic receipt will invalidate it
invalid_command(attest) The gate is not in the frozen contract / the gate does not declare allowOverride / an automated gate does not declare mandatorySignoff yet approval is attempted Check the frozen contract (ha task show); changing governance fields requires a new submit to take effect
invalid_transition(attest) No submitted execution / appliesTo mismatch / no automatic pass at approval time / an automatic witness already exists at override time First submit/complete so the witness is persisted; a genuine pass does not need special approval
actor_unauthorized(attest) A session signoff with an executor / a non-owner attempts to override Performed by a human principal; the override must be the task creator
invalid_field(attest) The override is missing --result pass or the rationale is less than 10 characters / approve incorrectly carries --rationale Fix it according to the command form
The task is stuck at incomplete, and the blocker is a gate That gate has no pass witness bound to the current cut Run ha task show to see the gate status; add evidence according to the adapter
A custom gate is mapped but never takes effect No task declares it For the preset's completionGates , add that id

Boundaries and cautions

  • The contract is frozen with the submission: changing harness.yaml only affects subsequent submits; an in-review cut is judged by the frozen contract. After the cut is amended, commitSha + submissionDigest changes, and old observations (including human signoffs and special approvals) are invalidated and must be recollected.
  • Historical conclusions retained through migration (preserved verdict: an old "accepted" with no bound evidence) are recognized only in lifecycle replay,and can never serve as a gate pass for the current cut—a new cut must collect evidence again or be signed off again.
  • Overview of the four forms: pure automatic witness (default),manual-attest pure human,mandatorySignoff automatic + signoff dual control,allowOverride automatic failure can be specially approved. The two governance fields can be attached to the same gate (dual control + special approval); a gate that has been specially approved is recorded as waived a satisfied state.