@mikenguyen69/harness 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/build-identity.json +1 -0
- package/dist/cancellation.js +138 -0
- package/dist/cli.js +47 -0
- package/dist/dispatch.js +70 -0
- package/dist/help.js +47 -0
- package/dist/internal/agent/adapter/conformance.js +53 -0
- package/dist/internal/agent/adapter/hook.js +113 -0
- package/dist/internal/agent/adapter/install.js +30 -0
- package/dist/internal/agent/adapter/scope.js +55 -0
- package/dist/internal/agent/adapter/session.js +63 -0
- package/dist/internal/agent/adapter/settings.js +51 -0
- package/dist/internal/agent/cli.js +79 -0
- package/dist/internal/agent/evals/index.js +111 -0
- package/dist/internal/agent/index.js +15 -0
- package/dist/internal/agent/ledger-read.js +27 -0
- package/dist/internal/agent/routing/cli.js +104 -0
- package/dist/internal/agent/routing/index.js +123 -0
- package/dist/internal/agent/telemetry/index.js +97 -0
- package/dist/internal/orchestration/activity/index.js +111 -0
- package/dist/internal/orchestration/analyze/index.js +237 -0
- package/dist/internal/orchestration/cli.js +475 -0
- package/dist/internal/orchestration/config/index.js +66 -0
- package/dist/internal/orchestration/doctor/index.js +202 -0
- package/dist/internal/orchestration/forge/index.js +121 -0
- package/dist/internal/orchestration/git/index.js +181 -0
- package/dist/internal/orchestration/harness/cli.js +378 -0
- package/dist/internal/orchestration/harness/index.js +186 -0
- package/dist/internal/orchestration/harness/shim.js +182 -0
- package/dist/internal/orchestration/index.js +29 -0
- package/dist/internal/orchestration/loop/index.js +1107 -0
- package/dist/internal/orchestration/prompts/index.js +199 -0
- package/dist/internal/orchestration/report/index.js +125 -0
- package/dist/internal/orchestration/routes/index.js +85 -0
- package/dist/internal/orchestration/run/index.js +221 -0
- package/dist/internal/orchestration/runner/index.js +90 -0
- package/dist/internal/orchestration/runners/claude/index.js +246 -0
- package/dist/internal/orchestration/runners/claude/sdk.js +198 -0
- package/dist/internal/orchestration/runners/claude/wiring.js +98 -0
- package/dist/internal/orchestration/runners/codex/exec.js +114 -0
- package/dist/internal/orchestration/runners/codex/index.js +72 -0
- package/dist/internal/orchestration/runners/conformance/index.js +169 -0
- package/dist/internal/orchestration/runners/cursor/exec.js +531 -0
- package/dist/internal/orchestration/runners/cursor/index.js +184 -0
- package/dist/internal/orchestration/runners/stub/index.js +168 -0
- package/dist/internal/orchestration/runners/telemetry.js +36 -0
- package/dist/internal/orchestration/runstate/index.js +213 -0
- package/dist/internal/orchestration/runstate/publish.js +115 -0
- package/dist/internal/orchestration/worktree/index.js +89 -0
- package/dist/internal/system/adapters/index.js +19 -0
- package/dist/internal/system/agentops/index.js +16 -0
- package/dist/internal/system/agentops/scope.js +67 -0
- package/dist/internal/system/assets.js +45 -0
- package/dist/internal/system/board/cli.js +56 -0
- package/dist/internal/system/board/index.js +276 -0
- package/dist/internal/system/board/live.js +277 -0
- package/dist/internal/system/calibration/cli.js +45 -0
- package/dist/internal/system/calibration/index.js +141 -0
- package/dist/internal/system/cli.js +174 -0
- package/dist/internal/system/corpus/replay.js +79 -0
- package/dist/internal/system/digest/cli.js +23 -0
- package/dist/internal/system/digest/index.js +145 -0
- package/dist/internal/system/doors/classify.js +94 -0
- package/dist/internal/system/doors/cli.js +184 -0
- package/dist/internal/system/doors/index.js +249 -0
- package/dist/internal/system/doors/metrics.js +47 -0
- package/dist/internal/system/escapes/cli.js +45 -0
- package/dist/internal/system/escapes/index.js +81 -0
- package/dist/internal/system/explore/cli.js +77 -0
- package/dist/internal/system/explore/index.js +91 -0
- package/dist/internal/system/gates/cli.js +43 -0
- package/dist/internal/system/gates/detectors.js +55 -0
- package/dist/internal/system/gates/pre.js +51 -0
- package/dist/internal/system/hub/cli.js +51 -0
- package/dist/internal/system/hub/index.js +146 -0
- package/dist/internal/system/init/index.js +83 -0
- package/dist/internal/system/ledger/cli.js +54 -0
- package/dist/internal/system/ledger/index.js +176 -0
- package/dist/internal/system/manifest/index.js +207 -0
- package/dist/internal/system/provenance/index.js +13 -0
- package/dist/internal/system/report/index.js +15 -0
- package/dist/internal/system/runner/index.js +74 -0
- package/dist/internal/system/runstatus/cli.js +86 -0
- package/dist/internal/system/runstatus/index.js +168 -0
- package/dist/internal/system/sequencer/briefing.js +75 -0
- package/dist/internal/system/sequencer/cli.js +170 -0
- package/dist/internal/system/sequencer/index.js +156 -0
- package/dist/internal/system/spec/cli.js +110 -0
- package/dist/internal/system/spec/index.js +212 -0
- package/dist/internal/system/spec/materialize.js +111 -0
- package/dist/internal/system/spec/openspec-parse.js +83 -0
- package/dist/internal/system/spec/project.js +208 -0
- package/dist/internal/system/substrate/index.js +14 -0
- package/dist/internal/system/system/index.js +159 -0
- package/dist/internal/system/units/index.js +87 -0
- package/dist/internal/system/verify/cli.js +103 -0
- package/dist/internal/system/verify/declined-reasons.js +61 -0
- package/dist/internal/system/verify/index.js +188 -0
- package/dist/internal/system/verify/signing.js +79 -0
- package/dist/paths.js +50 -0
- package/dist/routes.js +93 -0
- package/dist/version.js +30 -0
- package/dist/win32-process-group.js +360 -0
- package/package.json +41 -0
- package/profiles/python/bindings.toml +59 -0
- package/profiles/terraform/bindings.toml +66 -0
- package/profiles/typescript/bindings.toml +64 -0
- package/profiles/typescript/smoke/README.md +8 -0
- package/schema/baseline.schema.json +26 -0
- package/schema/decision-classes.yaml +45 -0
- package/schema/envelope.schema.json +186 -0
- package/schema/ledger-event.schema.json +235 -0
- package/schema/manifest.schema.json +119 -0
- package/schema/routes.schema.json +55 -0
- package/schema/run-status.schema.json +162 -0
- package/schema/session-event.schema.json +74 -0
- package/schema/spec.schema.json +114 -0
- package/schema/system.schema.json +142 -0
- package/templates/ci/verify.yml +23 -0
- package/templates/target-kit/README.md +78 -0
- package/templates/target-kit/component.toml +20 -0
- package/templates/target-kit/orchestration.json +9 -0
- package/templates/target-kit/profiles/target/bindings.toml +6 -0
- package/templates/target-kit/routes.toml +5 -0
- package/templates/target-kit/specs/first-unit.yaml +11 -0
- package/templates/target-kit/system.toml +7 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Terraform profile — the 10-name vocabulary (ADR 0004) bound to a Terraform
|
|
2
|
+
# stack. This file is the ENTIRE diff for a second stack: zero src changes.
|
|
3
|
+
#
|
|
4
|
+
# The hermetic rule: `terraform validate` and `fmt` are offline and safe in
|
|
5
|
+
# `verify`; anything that reaches a provider or state (`plan` against real infra)
|
|
6
|
+
# is marked external = true and runs only in the component's deploy phase.
|
|
7
|
+
|
|
8
|
+
[harness]
|
|
9
|
+
profile_version = "0.0.1"
|
|
10
|
+
|
|
11
|
+
[gates.spec]
|
|
12
|
+
run = "harness gate spec"
|
|
13
|
+
severity = "blocking"
|
|
14
|
+
cost = "cheap"
|
|
15
|
+
|
|
16
|
+
[gates.fmt]
|
|
17
|
+
run = "terraform fmt -check -recursive"
|
|
18
|
+
severity = "blocking"
|
|
19
|
+
cost = "cheap"
|
|
20
|
+
|
|
21
|
+
[gates.lint]
|
|
22
|
+
run = "tflint --format compact"
|
|
23
|
+
severity = "changed-lines"
|
|
24
|
+
cost = "cheap"
|
|
25
|
+
|
|
26
|
+
[gates.types]
|
|
27
|
+
run = "terraform validate" # offline: schema + reference checks
|
|
28
|
+
severity = "blocking"
|
|
29
|
+
cost = "cheap"
|
|
30
|
+
|
|
31
|
+
[gates.test]
|
|
32
|
+
run = "terraform test" # native test framework, module-scoped
|
|
33
|
+
severity = "advisory"
|
|
34
|
+
cost = "expensive"
|
|
35
|
+
|
|
36
|
+
[gates."sec:deps"]
|
|
37
|
+
run = "trivy config --exit-code 1 ."
|
|
38
|
+
severity = "changed-lines"
|
|
39
|
+
cost = "cheap"
|
|
40
|
+
snapshot_max_age_days = 7
|
|
41
|
+
|
|
42
|
+
[gates."sec:secrets"]
|
|
43
|
+
run = "gitleaks detect --no-banner"
|
|
44
|
+
severity = "blocking"
|
|
45
|
+
cost = "cheap"
|
|
46
|
+
|
|
47
|
+
[gates.arch]
|
|
48
|
+
run = "checkov -d . --compact --quiet"
|
|
49
|
+
severity = "advisory"
|
|
50
|
+
cost = "cheap"
|
|
51
|
+
|
|
52
|
+
[gates.contract]
|
|
53
|
+
run = "terraform plan -detailed-exitcode -lock=false" # reality check vs live state
|
|
54
|
+
severity = "advisory"
|
|
55
|
+
cost = "expensive"
|
|
56
|
+
external = true # deploy phase only — never in hermetic verify
|
|
57
|
+
|
|
58
|
+
[gates.size]
|
|
59
|
+
run = "harness gate size"
|
|
60
|
+
severity = "blocking"
|
|
61
|
+
cost = "cheap"
|
|
62
|
+
|
|
63
|
+
[gates."gen:fresh"]
|
|
64
|
+
run = "harness gate gen-fresh"
|
|
65
|
+
severity = "blocking"
|
|
66
|
+
cost = "cheap"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# TypeScript profile — reference bindings of the abstract vocabulary to real
|
|
2
|
+
# tools. Data, not code: a consuming repo's component.toml starts from this and
|
|
3
|
+
# overrides per module. Every vocabulary gate appears — bound or declined.
|
|
4
|
+
#
|
|
5
|
+
# Vocabulary: the 10-name closed set (ADR 0004) — spec fmt lint types test
|
|
6
|
+
# sec:* arch contract size gen:fresh. `sec:*` is a namespace: bind as many
|
|
7
|
+
# concrete sec:<tool> gates as the stack needs.
|
|
8
|
+
|
|
9
|
+
[harness]
|
|
10
|
+
profile_version = "0.0.2"
|
|
11
|
+
|
|
12
|
+
[gates.spec]
|
|
13
|
+
run = "harness gate spec" # built-in linter vs schema/spec.schema.json
|
|
14
|
+
severity = "blocking"
|
|
15
|
+
cost = "cheap"
|
|
16
|
+
|
|
17
|
+
[gates.fmt]
|
|
18
|
+
run = "prettier --check ."
|
|
19
|
+
severity = "blocking"
|
|
20
|
+
cost = "cheap"
|
|
21
|
+
|
|
22
|
+
[gates.lint]
|
|
23
|
+
run = "eslint ."
|
|
24
|
+
severity = "changed-lines"
|
|
25
|
+
cost = "cheap"
|
|
26
|
+
|
|
27
|
+
[gates.types]
|
|
28
|
+
run = "tsc --noEmit"
|
|
29
|
+
severity = "blocking"
|
|
30
|
+
cost = "cheap"
|
|
31
|
+
|
|
32
|
+
[gates.test]
|
|
33
|
+
run = "vitest run" # the component's own fast suite; true cross-
|
|
34
|
+
severity = "blocking" # component integration is the ring-3 e2e
|
|
35
|
+
cost = "cheap" # component (system.toml role="integration")
|
|
36
|
+
|
|
37
|
+
[gates."sec:deps"]
|
|
38
|
+
run = "osv-scanner --lockfile package-lock.json"
|
|
39
|
+
severity = "changed-lines"
|
|
40
|
+
cost = "cheap"
|
|
41
|
+
snapshot_max_age_days = 7 # vendored advisory DB; no network during verify
|
|
42
|
+
|
|
43
|
+
[gates."sec:secrets"]
|
|
44
|
+
run = "gitleaks detect --no-banner"
|
|
45
|
+
severity = "blocking"
|
|
46
|
+
cost = "cheap"
|
|
47
|
+
|
|
48
|
+
[gates.arch]
|
|
49
|
+
run = "dependency-cruiser --validate .dependency-cruiser.cjs src"
|
|
50
|
+
severity = "advisory"
|
|
51
|
+
cost = "cheap"
|
|
52
|
+
|
|
53
|
+
[gates.contract]
|
|
54
|
+
declined = "no service boundary in the reference profile; bind per repo (openapi-diff / buf breaking)"
|
|
55
|
+
|
|
56
|
+
[gates.size]
|
|
57
|
+
run = "harness gate size" # built-in: diff vs the unit's size_budget
|
|
58
|
+
severity = "blocking"
|
|
59
|
+
cost = "cheap"
|
|
60
|
+
|
|
61
|
+
[gates."gen:fresh"]
|
|
62
|
+
run = "harness gate gen-fresh" # built-in: generated adapters match policy hash
|
|
63
|
+
severity = "blocking"
|
|
64
|
+
cost = "cheap"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# TypeScript profile smoke test
|
|
2
|
+
|
|
3
|
+
A tiny fixture repo proving the profile end to end: `harness init` scaffolds
|
|
4
|
+
it, `harness verify` runs every bound gate against it and emits a valid
|
|
5
|
+
envelope. Runs in the harness's own CI on every profile change.
|
|
6
|
+
|
|
7
|
+
TODO(milestone 1): materialize the fixture (minimal package.json, one source
|
|
8
|
+
file, one test, one deliberate lint finding to prove severity handling).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://harness.local/schema/baseline.schema.json",
|
|
4
|
+
"title": "Brownfield baseline — frozen debt inventory (.harness/baseline.json)",
|
|
5
|
+
"description": "Written once by `harness init --audit`, then only ever lowered (by verify runs observing debt shrink) — never raised by hand. Serves two roles: the ratchet floor (repo debt totals may only fall) and the calibration baseline ('is the harness helping' is measured against this). Live secrets never appear here: they are incidents to fix before init, not debt to ratchet.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["captured_at", "harness_version", "debt"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"captured_at": { "type": "string", "format": "date-time" },
|
|
10
|
+
"harness_version": { "type": "string" },
|
|
11
|
+
"debt": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"description": "per-gate frozen finding counts, e.g. { \"lint\": 1847, \"types\": 612, \"fmt\": 311 }",
|
|
14
|
+
"additionalProperties": { "type": "integer", "minimum": 0 }
|
|
15
|
+
},
|
|
16
|
+
"outcome_baseline": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "pre-harness delivery metrics for calibration (capture ~1 day of effort; without these, measurement can only report gate activity, never value)",
|
|
19
|
+
"properties": {
|
|
20
|
+
"escaped_defects_per_week": { "type": "number" },
|
|
21
|
+
"rework_rate": { "type": "number" },
|
|
22
|
+
"review_cycle_hours": { "type": "number" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Decision classes — what always requires a human (v1 trust boundary).
|
|
2
|
+
# Policy data, versioned and reviewed like the gate vocabulary.
|
|
3
|
+
# Everything NOT listed is eligible for agent review once the reviewer has
|
|
4
|
+
# earned it (seeded-defect catch rate — see calibration/).
|
|
5
|
+
# Revisit as data accumulates; changes land as reviewed PRs, never inline.
|
|
6
|
+
#
|
|
7
|
+
# v2 (system-harness-blueprint.md §3): decision classes gain system scope —
|
|
8
|
+
# rules that only exist once the system is a component DAG (system.toml).
|
|
9
|
+
|
|
10
|
+
version: 2
|
|
11
|
+
|
|
12
|
+
human_required:
|
|
13
|
+
- id: one-way-door
|
|
14
|
+
match: "unit.door == 'one-way' or unit.door unset"
|
|
15
|
+
why: irreversible decisions get a human by definition
|
|
16
|
+
- id: new-dependency
|
|
17
|
+
match: "diff adds entry to lockfile/manifest (package.json, go.mod, requirements, ...)"
|
|
18
|
+
why: supply-chain surface; agents pull packages without risk review
|
|
19
|
+
- id: schema-or-api-change
|
|
20
|
+
match: "diff touches API contracts, migrations, protobuf/openapi files"
|
|
21
|
+
why: breaks ripple across boundaries; contract gate catches compat, not intent
|
|
22
|
+
- id: security-touching
|
|
23
|
+
match: "diff touches auth, crypto, secrets handling, permission checks"
|
|
24
|
+
why: highest blast radius, weakest test coverage in most repos
|
|
25
|
+
|
|
26
|
+
system_scope:
|
|
27
|
+
# Classes that exist only under Contract C (system.toml). Evaluated by
|
|
28
|
+
# `harness verify --system` (blueprint §4 ring 2) in addition to per-unit review.
|
|
29
|
+
- id: infra-apply
|
|
30
|
+
match: "unit.component's profile applies external infrastructure (e.g. terraform apply)"
|
|
31
|
+
door: one-way
|
|
32
|
+
why: infrastructure applies are irreversible by definition — write_external, always a human
|
|
33
|
+
- id: cross-component-api-change
|
|
34
|
+
match: "diff changes an interface a downstream component consumes (published artifact schema, cross-component API)"
|
|
35
|
+
why: breaks ripple across the component boundary; the contract gate catches compat, not intent
|
|
36
|
+
- id: producer-artifact-diff
|
|
37
|
+
match: "diff touches a published artifact's producer"
|
|
38
|
+
effect: re-verify all consumer components before merge
|
|
39
|
+
why: a stale artifact hash is a fail-closed condition, not a warning
|
|
40
|
+
|
|
41
|
+
anti_rubber_stamp:
|
|
42
|
+
# A door approved >95% of the time within seconds is measurably dead:
|
|
43
|
+
# flag for demotion to two-way or redesign. Calibration job enforces.
|
|
44
|
+
approval_rate_threshold: 0.95
|
|
45
|
+
min_deliberation_seconds: 30
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://harness.local/schema/envelope.schema.json",
|
|
4
|
+
"title": "Contract B — verify report envelope (component-first)",
|
|
5
|
+
"description": "One envelope per `harness verify` run, wrapping one SARIF file. The envelope IS the provenance record: work check, merge doors, CI, and calibration jobs all consume this same artifact. Honesty rules: retries are recorded per gate (a pass-on-retry is visibly not a clean pass); agent identity is self-reported until signed attestations land. Extended per system-harness-blueprint.md §3 (\"Contract B — envelope (extended)\"): every envelope names its `component_id`; `feature_id` is the top-level trace linking all units of one feature across components (null for standalone units); `artifacts` records which published contract values the unit was built against. Extended again by the review pass: `session_id` joins fleet telemetry (Contract D); `scope_attested` is the harness-computed diff-in-scope check; `signature` is the harness-key signature that makes envelopes unforgeable; per-gate `pre_failures` counts ring-0 catches.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"envelope_version",
|
|
9
|
+
"harness_version",
|
|
10
|
+
"policy_version",
|
|
11
|
+
"profile_version",
|
|
12
|
+
"binding_hash",
|
|
13
|
+
"component_id",
|
|
14
|
+
"unit_id",
|
|
15
|
+
"spec_hash",
|
|
16
|
+
"agent_identity",
|
|
17
|
+
"started_at",
|
|
18
|
+
"gates",
|
|
19
|
+
"verdict",
|
|
20
|
+
"sarif_path",
|
|
21
|
+
"scope_attested",
|
|
22
|
+
"signature"
|
|
23
|
+
],
|
|
24
|
+
"properties": {
|
|
25
|
+
"envelope_version": { "type": "string" },
|
|
26
|
+
"session_id": {
|
|
27
|
+
"type": ["string", "null"],
|
|
28
|
+
"description": "the agent-harness session that produced this run (Contract D); null for human/ci runs. This is the join key for fleet telemetry — system-harness never reads it back to decide a merge (solution design D2)."
|
|
29
|
+
},
|
|
30
|
+
"scope_attested": {
|
|
31
|
+
"type": "boolean",
|
|
32
|
+
"description": "true iff the diff is a subset of the unit's declared scope. Computed from the diff by the harness — never asserted by the agent. A false value is a fail-closed condition."
|
|
33
|
+
},
|
|
34
|
+
"scope_evidence": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"description": "signed inputs and result of the harness-computed diff-in-scope check",
|
|
37
|
+
"required": ["base_ref", "changed_files", "effective_scope", "violations", "reason"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"base_ref": { "type": ["string", "null"] },
|
|
40
|
+
"changed_files": { "type": "array", "items": { "type": "string" } },
|
|
41
|
+
"effective_scope": { "type": "array", "items": { "type": "string" } },
|
|
42
|
+
"violations": { "type": "array", "items": { "type": "string" } },
|
|
43
|
+
"reason": { "type": ["string", "null"] }
|
|
44
|
+
},
|
|
45
|
+
"additionalProperties": false
|
|
46
|
+
},
|
|
47
|
+
"signature": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "detached signature over the canonical envelope body (every field except this one) by the harness binary key. Agents cannot mint envelopes; `agent_identity.attested` is a weaker self-report and is orthogonal."
|
|
50
|
+
},
|
|
51
|
+
"harness_version": { "type": "string", "description": "binary semver" },
|
|
52
|
+
"policy_version": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "policy-data semver in force"
|
|
55
|
+
},
|
|
56
|
+
"profile_version": { "type": "string" },
|
|
57
|
+
"binding_hash": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "hash of the resolved gate bindings — answers 'which rules were actually in force for this merge'"
|
|
60
|
+
},
|
|
61
|
+
"component_id": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "the component this run verified (must exist in system.toml); every envelope names its component — blueprint §3"
|
|
64
|
+
},
|
|
65
|
+
"feature_id": {
|
|
66
|
+
"type": ["string", "null"],
|
|
67
|
+
"description": "top-level trace linking all units of one feature across components; null for standalone units. This is where product-harness's story-mapped feature ids land — no new field needed later."
|
|
68
|
+
},
|
|
69
|
+
"unit_id": { "type": "string" },
|
|
70
|
+
"spec_hash": { "type": "string" },
|
|
71
|
+
"artifacts": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"description": "typed handoffs this run produced or built against — proves which published contract values were in force (blueprint §3). Optional: absent when the component neither produces nor consumes artifacts.",
|
|
74
|
+
"properties": {
|
|
75
|
+
"produced": {
|
|
76
|
+
"type": "array",
|
|
77
|
+
"items": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"required": ["name", "hash"],
|
|
80
|
+
"properties": {
|
|
81
|
+
"name": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "artifact name, as declared in system.toml"
|
|
84
|
+
},
|
|
85
|
+
"hash": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "content hash of the published artifact value"
|
|
88
|
+
},
|
|
89
|
+
"path": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "path the artifact was written to"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"additionalProperties": false
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"consumed": {
|
|
98
|
+
"type": "array",
|
|
99
|
+
"items": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"required": ["name", "hash"],
|
|
102
|
+
"properties": {
|
|
103
|
+
"name": { "type": "string" },
|
|
104
|
+
"hash": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "hash of the producer's value this unit built against; `verify --system` halts if it is stale"
|
|
107
|
+
},
|
|
108
|
+
"path": { "type": "string" }
|
|
109
|
+
},
|
|
110
|
+
"additionalProperties": false
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"additionalProperties": false
|
|
115
|
+
},
|
|
116
|
+
"agent_identity": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"required": ["kind", "id", "attested"],
|
|
119
|
+
"properties": {
|
|
120
|
+
"kind": { "enum": ["agent", "human", "ci"] },
|
|
121
|
+
"id": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "e.g. claude-fable-5, cursor+model, git author"
|
|
124
|
+
},
|
|
125
|
+
"attested": {
|
|
126
|
+
"type": "boolean",
|
|
127
|
+
"description": "false = self-reported (provenance-flavored, not provenance)"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
"started_at": { "type": "string", "format": "date-time" },
|
|
132
|
+
"duration_ms": { "type": "integer" },
|
|
133
|
+
"gates": {
|
|
134
|
+
"type": "array",
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"required": ["name", "outcome", "severity", "duration_ms", "retries"],
|
|
138
|
+
"properties": {
|
|
139
|
+
"name": { "type": "string" },
|
|
140
|
+
"outcome": { "enum": ["pass", "fail", "declined", "quarantined"] },
|
|
141
|
+
"severity": { "enum": ["advisory", "changed-lines", "blocking"] },
|
|
142
|
+
"duration_ms": { "type": "integer" },
|
|
143
|
+
"retries": {
|
|
144
|
+
"type": "integer",
|
|
145
|
+
"minimum": 0,
|
|
146
|
+
"description": ">0 means pass-on-retry — never a clean pass"
|
|
147
|
+
},
|
|
148
|
+
"pre_failures": {
|
|
149
|
+
"type": "integer",
|
|
150
|
+
"minimum": 0,
|
|
151
|
+
"description": "ring-0 (`gate --pre`) failures for this gate absorbed in-loop before this ring-1 run — the evidence of ring-0's value; feeds calibration and evals"
|
|
152
|
+
},
|
|
153
|
+
"declined_reason": { "type": "string" },
|
|
154
|
+
"quarantine_expires": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"format": "date-time",
|
|
157
|
+
"description": "flaky gates are quarantined with an expiry, never silently rerun"
|
|
158
|
+
},
|
|
159
|
+
"ratchet": {
|
|
160
|
+
"type": "object",
|
|
161
|
+
"description": "present in ratchet mode: the frozen debt floor and this run's observed total — current > floor fails even at changed-lines severity; current < floor lowers the floor",
|
|
162
|
+
"required": ["floor", "current"],
|
|
163
|
+
"properties": {
|
|
164
|
+
"floor": { "type": "integer", "minimum": 0 },
|
|
165
|
+
"current": { "type": "integer", "minimum": 0 }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
"substrate": {
|
|
172
|
+
"type": "object",
|
|
173
|
+
"description": "self-report of the guarantee layer: when server-side enforcement is not yet active (dated cutover on brownfield repos), verify declares itself ADVISORY at the repo level rather than letting the guarantee be silently absent",
|
|
174
|
+
"required": ["enforced"],
|
|
175
|
+
"properties": {
|
|
176
|
+
"enforced": { "type": "boolean" },
|
|
177
|
+
"enforced_from": { "type": "string", "format": "date" }
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"verdict": {
|
|
181
|
+
"enum": ["pass", "fail", "halt"],
|
|
182
|
+
"description": "halt = fail-closed condition (ambiguity, out-of-scope touch, all-skip). All-declined/all-skip is never 'pass'."
|
|
183
|
+
},
|
|
184
|
+
"sarif_path": { "type": "string" }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://harness.local/schema/ledger-event.schema.json",
|
|
4
|
+
"title": "Ledger event — one line of .harness/ledger/*.jsonl",
|
|
5
|
+
"description": "The record plane (solution design §5). One append-only, hash-chained event ledger per system is the single source of truth; the board, the hub index and calibration input are all derived and rebuildable. Each event is one JSON object on one line. `prev_hash` chains every event to its predecessor and `hash` is over this event's canonical form (all fields except `hash`), so any deletion or rewrite is evident at `harness ledger verify-chain` and at hub sync. system-harness emits spec.* unit.* envelope door.* system.* escape.* fixture.* calibration.*; agent-harness (Contract D) emits session.* eval.* through the same `harness ledger append` so chaining and signing stay in one implementation.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"v",
|
|
9
|
+
"seq",
|
|
10
|
+
"ts",
|
|
11
|
+
"system",
|
|
12
|
+
"actor",
|
|
13
|
+
"type",
|
|
14
|
+
"prev_hash",
|
|
15
|
+
"hash",
|
|
16
|
+
"body"
|
|
17
|
+
],
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"v": { "const": 1, "description": "ledger-event schema major version" },
|
|
21
|
+
"seq": {
|
|
22
|
+
"type": "integer",
|
|
23
|
+
"minimum": 0,
|
|
24
|
+
"description": "0-based position in the ledger; strictly increasing, no gaps. seq 0 is the genesis event."
|
|
25
|
+
},
|
|
26
|
+
"ts": { "type": "string", "format": "date-time" },
|
|
27
|
+
"system": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 1,
|
|
30
|
+
"description": "system id — one ledger per system"
|
|
31
|
+
},
|
|
32
|
+
"actor": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": ["kind", "id"],
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"properties": {
|
|
37
|
+
"kind": { "enum": ["harness", "agent", "human", "ci"] },
|
|
38
|
+
"id": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"minLength": 1,
|
|
41
|
+
"description": "e.g. harness@1.2.0, claude-fable-5, git author, ci-runner"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"type": {
|
|
46
|
+
"enum": [
|
|
47
|
+
"spec.authored",
|
|
48
|
+
"spec.validated",
|
|
49
|
+
"spec.amended",
|
|
50
|
+
"unit.planned",
|
|
51
|
+
"unit.ready",
|
|
52
|
+
"unit.claimed",
|
|
53
|
+
"unit.done",
|
|
54
|
+
"unit.merged",
|
|
55
|
+
"unit.aborted",
|
|
56
|
+
"session.started",
|
|
57
|
+
"session.heartbeat",
|
|
58
|
+
"session.gate_fired",
|
|
59
|
+
"session.escalated",
|
|
60
|
+
"session.ended",
|
|
61
|
+
"envelope",
|
|
62
|
+
"door.opened",
|
|
63
|
+
"door.decided",
|
|
64
|
+
"door.expired",
|
|
65
|
+
"system.verified",
|
|
66
|
+
"system.halted",
|
|
67
|
+
"escape.detected",
|
|
68
|
+
"escape.routed",
|
|
69
|
+
"fixture.added",
|
|
70
|
+
"fixture.passed",
|
|
71
|
+
"fixture.reopened",
|
|
72
|
+
"eval.run",
|
|
73
|
+
"calibration.proposed",
|
|
74
|
+
"calibration.applied",
|
|
75
|
+
"run.analyzed"
|
|
76
|
+
],
|
|
77
|
+
"description": "event type. `envelope` is a single type (not `envelope.*`) whose body is a full Contract B envelope."
|
|
78
|
+
},
|
|
79
|
+
"prev_hash": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
82
|
+
"description": "SHA-256 hex of the previous event's `hash`. The genesis event (seq 0) uses 64 zeros."
|
|
83
|
+
},
|
|
84
|
+
"hash": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
87
|
+
"description": "SHA-256 hex over the canonical JSON of this event with the `hash` key removed (so `prev_hash` is covered). verify-chain recomputes and compares every line."
|
|
88
|
+
},
|
|
89
|
+
"sig": {
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "detached signature by the harness binary key. REQUIRED on `envelope` events — agents cannot mint them. Optional on other event types in v1."
|
|
92
|
+
},
|
|
93
|
+
"body": {
|
|
94
|
+
"type": "object",
|
|
95
|
+
"description": "event-specific payload. A few types are constrained below; the rest follow the taxonomy in solution design §5.1 and are tightened as their producers land."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"allOf": [
|
|
99
|
+
{
|
|
100
|
+
"if": {
|
|
101
|
+
"properties": { "type": { "const": "envelope" } },
|
|
102
|
+
"required": ["type"]
|
|
103
|
+
},
|
|
104
|
+
"then": {
|
|
105
|
+
"required": ["sig"],
|
|
106
|
+
"properties": { "body": { "$ref": "envelope.schema.json" } }
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"if": {
|
|
111
|
+
"properties": { "type": { "const": "system.halted" } },
|
|
112
|
+
"required": ["type"]
|
|
113
|
+
},
|
|
114
|
+
"then": {
|
|
115
|
+
"properties": {
|
|
116
|
+
"body": {
|
|
117
|
+
"type": "object",
|
|
118
|
+
"required": ["cause"],
|
|
119
|
+
"properties": {
|
|
120
|
+
"cause": {
|
|
121
|
+
"enum": ["stale_artifact", "scope_cross", "dag_cycle"]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"if": {
|
|
130
|
+
"properties": { "type": { "const": "door.decided" } },
|
|
131
|
+
"required": ["type"]
|
|
132
|
+
},
|
|
133
|
+
"then": {
|
|
134
|
+
"properties": {
|
|
135
|
+
"body": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"required": ["verdict", "reason", "who"],
|
|
138
|
+
"properties": {
|
|
139
|
+
"verdict": { "enum": ["approved", "rejected"] },
|
|
140
|
+
"reason": { "type": "string", "minLength": 1 },
|
|
141
|
+
"who": { "type": "string", "minLength": 1 }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"if": {
|
|
149
|
+
"properties": { "type": { "const": "calibration.proposed" } },
|
|
150
|
+
"required": ["type"]
|
|
151
|
+
},
|
|
152
|
+
"then": {
|
|
153
|
+
"properties": {
|
|
154
|
+
"body": {
|
|
155
|
+
"type": "object",
|
|
156
|
+
"required": ["action", "target"],
|
|
157
|
+
"properties": {
|
|
158
|
+
"action": { "enum": ["promote", "demote", "delete"] },
|
|
159
|
+
"target": { "type": "string" }
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"if": {
|
|
167
|
+
"properties": { "type": { "const": "eval.run" } },
|
|
168
|
+
"required": ["type"]
|
|
169
|
+
},
|
|
170
|
+
"then": {
|
|
171
|
+
"properties": {
|
|
172
|
+
"body": {
|
|
173
|
+
"type": "object",
|
|
174
|
+
"required": ["route", "catch_rate", "n"],
|
|
175
|
+
"properties": {
|
|
176
|
+
"route": { "type": "string" },
|
|
177
|
+
"catch_rate": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
178
|
+
"n": { "type": "integer", "minimum": 0 }
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"if": {
|
|
186
|
+
"properties": { "type": { "const": "run.analyzed" } },
|
|
187
|
+
"required": ["type"]
|
|
188
|
+
},
|
|
189
|
+
"then": {
|
|
190
|
+
"properties": {
|
|
191
|
+
"body": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"required": ["run_id", "units", "findings", "log_ref"],
|
|
194
|
+
"description": "one bounded summary per orchestration run — metrics and rule ids only, no free-text prose (see the description on run.analyzed in ledger/index.ts).",
|
|
195
|
+
"properties": {
|
|
196
|
+
"run_id": { "type": "string", "minLength": 1 },
|
|
197
|
+
"units": {
|
|
198
|
+
"type": "array",
|
|
199
|
+
"items": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"required": ["unit_id", "turns_total", "attempts", "wall_ms"],
|
|
202
|
+
"properties": {
|
|
203
|
+
"unit_id": { "type": "string" },
|
|
204
|
+
"turns_total": { "type": "integer", "minimum": 0 },
|
|
205
|
+
"tool_failures": { "type": "integer", "minimum": 0 },
|
|
206
|
+
"attempts": { "type": "integer", "minimum": 0 },
|
|
207
|
+
"wall_ms": { "type": "integer", "minimum": 0 },
|
|
208
|
+
"agent_ms": { "type": "integer", "minimum": 0 },
|
|
209
|
+
"cost_usd": { "type": "number", "minimum": 0 },
|
|
210
|
+
"runner": { "type": "string" },
|
|
211
|
+
"model": { "type": "string" }
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"findings": {
|
|
216
|
+
"type": "array",
|
|
217
|
+
"items": {
|
|
218
|
+
"type": "object",
|
|
219
|
+
"required": ["rule", "unit_id", "severity"],
|
|
220
|
+
"properties": {
|
|
221
|
+
"rule": { "type": "string" },
|
|
222
|
+
"unit_id": { "type": "string" },
|
|
223
|
+
"severity": { "enum": ["info", "warn", "action"] },
|
|
224
|
+
"count": { "type": "integer", "minimum": 0 }
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"log_ref": { "type": "string" }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
}
|