@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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/dist/build-identity.json +1 -0
  4. package/dist/cancellation.js +138 -0
  5. package/dist/cli.js +47 -0
  6. package/dist/dispatch.js +70 -0
  7. package/dist/help.js +47 -0
  8. package/dist/internal/agent/adapter/conformance.js +53 -0
  9. package/dist/internal/agent/adapter/hook.js +113 -0
  10. package/dist/internal/agent/adapter/install.js +30 -0
  11. package/dist/internal/agent/adapter/scope.js +55 -0
  12. package/dist/internal/agent/adapter/session.js +63 -0
  13. package/dist/internal/agent/adapter/settings.js +51 -0
  14. package/dist/internal/agent/cli.js +79 -0
  15. package/dist/internal/agent/evals/index.js +111 -0
  16. package/dist/internal/agent/index.js +15 -0
  17. package/dist/internal/agent/ledger-read.js +27 -0
  18. package/dist/internal/agent/routing/cli.js +104 -0
  19. package/dist/internal/agent/routing/index.js +123 -0
  20. package/dist/internal/agent/telemetry/index.js +97 -0
  21. package/dist/internal/orchestration/activity/index.js +111 -0
  22. package/dist/internal/orchestration/analyze/index.js +237 -0
  23. package/dist/internal/orchestration/cli.js +475 -0
  24. package/dist/internal/orchestration/config/index.js +66 -0
  25. package/dist/internal/orchestration/doctor/index.js +202 -0
  26. package/dist/internal/orchestration/forge/index.js +121 -0
  27. package/dist/internal/orchestration/git/index.js +181 -0
  28. package/dist/internal/orchestration/harness/cli.js +378 -0
  29. package/dist/internal/orchestration/harness/index.js +186 -0
  30. package/dist/internal/orchestration/harness/shim.js +182 -0
  31. package/dist/internal/orchestration/index.js +29 -0
  32. package/dist/internal/orchestration/loop/index.js +1107 -0
  33. package/dist/internal/orchestration/prompts/index.js +199 -0
  34. package/dist/internal/orchestration/report/index.js +125 -0
  35. package/dist/internal/orchestration/routes/index.js +85 -0
  36. package/dist/internal/orchestration/run/index.js +221 -0
  37. package/dist/internal/orchestration/runner/index.js +90 -0
  38. package/dist/internal/orchestration/runners/claude/index.js +246 -0
  39. package/dist/internal/orchestration/runners/claude/sdk.js +198 -0
  40. package/dist/internal/orchestration/runners/claude/wiring.js +98 -0
  41. package/dist/internal/orchestration/runners/codex/exec.js +114 -0
  42. package/dist/internal/orchestration/runners/codex/index.js +72 -0
  43. package/dist/internal/orchestration/runners/conformance/index.js +169 -0
  44. package/dist/internal/orchestration/runners/cursor/exec.js +531 -0
  45. package/dist/internal/orchestration/runners/cursor/index.js +184 -0
  46. package/dist/internal/orchestration/runners/stub/index.js +168 -0
  47. package/dist/internal/orchestration/runners/telemetry.js +36 -0
  48. package/dist/internal/orchestration/runstate/index.js +213 -0
  49. package/dist/internal/orchestration/runstate/publish.js +115 -0
  50. package/dist/internal/orchestration/worktree/index.js +89 -0
  51. package/dist/internal/system/adapters/index.js +19 -0
  52. package/dist/internal/system/agentops/index.js +16 -0
  53. package/dist/internal/system/agentops/scope.js +67 -0
  54. package/dist/internal/system/assets.js +45 -0
  55. package/dist/internal/system/board/cli.js +56 -0
  56. package/dist/internal/system/board/index.js +276 -0
  57. package/dist/internal/system/board/live.js +277 -0
  58. package/dist/internal/system/calibration/cli.js +45 -0
  59. package/dist/internal/system/calibration/index.js +141 -0
  60. package/dist/internal/system/cli.js +174 -0
  61. package/dist/internal/system/corpus/replay.js +79 -0
  62. package/dist/internal/system/digest/cli.js +23 -0
  63. package/dist/internal/system/digest/index.js +145 -0
  64. package/dist/internal/system/doors/classify.js +94 -0
  65. package/dist/internal/system/doors/cli.js +184 -0
  66. package/dist/internal/system/doors/index.js +249 -0
  67. package/dist/internal/system/doors/metrics.js +47 -0
  68. package/dist/internal/system/escapes/cli.js +45 -0
  69. package/dist/internal/system/escapes/index.js +81 -0
  70. package/dist/internal/system/explore/cli.js +77 -0
  71. package/dist/internal/system/explore/index.js +91 -0
  72. package/dist/internal/system/gates/cli.js +43 -0
  73. package/dist/internal/system/gates/detectors.js +55 -0
  74. package/dist/internal/system/gates/pre.js +51 -0
  75. package/dist/internal/system/hub/cli.js +51 -0
  76. package/dist/internal/system/hub/index.js +146 -0
  77. package/dist/internal/system/init/index.js +83 -0
  78. package/dist/internal/system/ledger/cli.js +54 -0
  79. package/dist/internal/system/ledger/index.js +176 -0
  80. package/dist/internal/system/manifest/index.js +207 -0
  81. package/dist/internal/system/provenance/index.js +13 -0
  82. package/dist/internal/system/report/index.js +15 -0
  83. package/dist/internal/system/runner/index.js +74 -0
  84. package/dist/internal/system/runstatus/cli.js +86 -0
  85. package/dist/internal/system/runstatus/index.js +168 -0
  86. package/dist/internal/system/sequencer/briefing.js +75 -0
  87. package/dist/internal/system/sequencer/cli.js +170 -0
  88. package/dist/internal/system/sequencer/index.js +156 -0
  89. package/dist/internal/system/spec/cli.js +110 -0
  90. package/dist/internal/system/spec/index.js +212 -0
  91. package/dist/internal/system/spec/materialize.js +111 -0
  92. package/dist/internal/system/spec/openspec-parse.js +83 -0
  93. package/dist/internal/system/spec/project.js +208 -0
  94. package/dist/internal/system/substrate/index.js +14 -0
  95. package/dist/internal/system/system/index.js +159 -0
  96. package/dist/internal/system/units/index.js +87 -0
  97. package/dist/internal/system/verify/cli.js +103 -0
  98. package/dist/internal/system/verify/declined-reasons.js +61 -0
  99. package/dist/internal/system/verify/index.js +188 -0
  100. package/dist/internal/system/verify/signing.js +79 -0
  101. package/dist/paths.js +50 -0
  102. package/dist/routes.js +93 -0
  103. package/dist/version.js +30 -0
  104. package/dist/win32-process-group.js +360 -0
  105. package/package.json +41 -0
  106. package/profiles/python/bindings.toml +59 -0
  107. package/profiles/terraform/bindings.toml +66 -0
  108. package/profiles/typescript/bindings.toml +64 -0
  109. package/profiles/typescript/smoke/README.md +8 -0
  110. package/schema/baseline.schema.json +26 -0
  111. package/schema/decision-classes.yaml +45 -0
  112. package/schema/envelope.schema.json +186 -0
  113. package/schema/ledger-event.schema.json +235 -0
  114. package/schema/manifest.schema.json +119 -0
  115. package/schema/routes.schema.json +55 -0
  116. package/schema/run-status.schema.json +162 -0
  117. package/schema/session-event.schema.json +74 -0
  118. package/schema/spec.schema.json +114 -0
  119. package/schema/system.schema.json +142 -0
  120. package/templates/ci/verify.yml +23 -0
  121. package/templates/target-kit/README.md +78 -0
  122. package/templates/target-kit/component.toml +20 -0
  123. package/templates/target-kit/orchestration.json +9 -0
  124. package/templates/target-kit/profiles/target/bindings.toml +6 -0
  125. package/templates/target-kit/routes.toml +5 -0
  126. package/templates/target-kit/specs/first-unit.yaml +11 -0
  127. 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
+ }