@kontourai/flow-agents 1.2.0 → 1.4.0

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 (95) hide show
  1. package/.github/workflows/ci.yml +6 -1
  2. package/.github/workflows/kit-gates-demo.yml +6 -2
  3. package/CHANGELOG.md +33 -0
  4. package/CONTRIBUTING.md +30 -0
  5. package/agents/dev.json +1 -1
  6. package/agents/tool-planner.json +1 -1
  7. package/build/src/cli/console-learning-projection.d.ts +1 -0
  8. package/build/src/cli/effective-backlog-settings.d.ts +1 -0
  9. package/build/src/cli/fixture-retirement-audit.d.ts +2 -0
  10. package/build/src/cli/init.d.ts +17 -0
  11. package/build/src/cli/kit.d.ts +1 -0
  12. package/build/src/cli/promote-workflow-artifact.d.ts +1 -0
  13. package/build/src/cli/publish-change-helper.d.ts +1 -0
  14. package/build/src/cli/pull-work-provider.d.ts +1 -0
  15. package/build/src/cli/runtime-adapter.d.ts +1 -0
  16. package/build/src/cli/telemetry-doctor.d.ts +1 -0
  17. package/build/src/cli/usage-feedback.d.ts +1 -0
  18. package/build/src/cli/utterance-check.d.ts +1 -0
  19. package/build/src/cli/validate-hook-influence.d.ts +1 -0
  20. package/build/src/cli/validate-source-tree.d.ts +1 -0
  21. package/build/src/cli/validate-workflow-artifacts.d.ts +2 -0
  22. package/build/src/cli/veritas-governance.d.ts +1 -0
  23. package/build/src/cli/workflow-artifact-cleanup-audit.d.ts +1 -0
  24. package/build/src/cli/workflow-sidecar.d.ts +32 -0
  25. package/build/src/cli/workflow-sidecar.js +119 -22
  26. package/build/src/cli.d.ts +2 -0
  27. package/build/src/flow-kit/validate.d.ts +81 -0
  28. package/build/src/flow-kit/validate.js +32 -1
  29. package/build/src/index.d.ts +5 -0
  30. package/build/src/index.js +36 -0
  31. package/build/src/lib/args.d.ts +8 -0
  32. package/build/src/lib/fs.d.ts +7 -0
  33. package/build/src/lib/workflow-learning-projection.d.ts +132 -0
  34. package/build/src/runtime-adapters.d.ts +18 -0
  35. package/build/src/tools/build-universal-bundles.d.ts +2 -0
  36. package/build/src/tools/build-universal-bundles.js +14 -0
  37. package/build/src/tools/common.d.ts +9 -0
  38. package/build/src/tools/filter-installed-packs.d.ts +2 -0
  39. package/build/src/tools/generate-context-map.d.ts +2 -0
  40. package/build/src/tools/validate-package.d.ts +2 -0
  41. package/build/src/tools/validate-source-tree.d.ts +2 -0
  42. package/console.telemetry.json +1 -1
  43. package/docs/adr/0004-gates-expect-surface-claims.md +7 -7
  44. package/docs/developer-architecture.md +14 -0
  45. package/docs/kit-authoring-guide.md +99 -6
  46. package/docs/operating-layers.md +2 -2
  47. package/docs/spec/runtime-hook-surface.md +16 -1
  48. package/docs/veritas-integration.md +4 -4
  49. package/docs/workflow-eval-strategy.md +2 -2
  50. package/docs/workflow-usage-guide.md +1 -1
  51. package/evals/acceptance/test_opencode_harness.sh +18 -10
  52. package/evals/acceptance/test_pi_harness.sh +10 -6
  53. package/evals/ci/run-baseline.sh +1 -1
  54. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/flows/runtime.flow.json +4 -4
  55. package/evals/fixtures/flow-kit-repository/valid-local-kit/flows/review.flow.json +4 -4
  56. package/evals/fixtures/kit-conformance-levels/k0-flows-only/flows/review.flow.json +4 -4
  57. package/evals/fixtures/kit-conformance-levels/k1-agent-extension/flows/build.flow.json +4 -4
  58. package/evals/fixtures/kit-conformance-levels/k2-with-evals/flows/synthesize.flow.json +4 -4
  59. package/evals/fixtures/kit-conformance-levels/third-party-extension/flows/review.flow.json +4 -4
  60. package/evals/fixtures/surface-trust/accepted-claim-trust-report.json +2 -2
  61. package/evals/fixtures/surface-trust/artifact-absent.json +2 -2
  62. package/evals/fixtures/surface-trust/integrity-mismatch-trust-report.json +2 -2
  63. package/evals/fixtures/surface-trust/missing-authority-trust-report.json +2 -2
  64. package/evals/fixtures/surface-trust/provider-absent.json +2 -2
  65. package/evals/fixtures/surface-trust/rejected-claim-trust-report.json +2 -2
  66. package/evals/fixtures/surface-trust/stale-claim-trust-snapshot.json +2 -2
  67. package/evals/integration/test_console_learning_projection.sh +1 -1
  68. package/evals/integration/test_goal_fit_hook.sh +144 -0
  69. package/evals/integration/test_hook_category_behaviors.sh +14 -0
  70. package/evals/integration/test_kit_conformance_levels.sh +55 -1
  71. package/evals/integration/test_workflow_sidecar_writer.sh +9 -9
  72. package/evals/run.sh +2 -0
  73. package/evals/static/test_library_exports.sh +85 -0
  74. package/evals/static/test_package.sh +3 -3
  75. package/evals/static/test_universal_bundles.sh +15 -0
  76. package/evals/static/test_workflow_skills.sh +4 -4
  77. package/kits/builder/flows/build.flow.json +48 -48
  78. package/kits/builder/flows/shape.flow.json +36 -36
  79. package/kits/knowledge/adapters/obsidian-store/index.js +137 -26
  80. package/kits/knowledge/evals/contract-suite/suite.test.js +90 -0
  81. package/kits/knowledge/flows/compile.flow.json +12 -12
  82. package/kits/knowledge/flows/consolidate.flow.json +16 -16
  83. package/kits/knowledge/flows/ingest.flow.json +12 -12
  84. package/kits/knowledge/flows/retire.flow.json +16 -16
  85. package/kits/knowledge/flows/store-contract.flow.json +12 -12
  86. package/kits/knowledge/flows/synthesize.flow.json +16 -16
  87. package/kits/release-evidence/flows/release-evidence.flow.json +3 -3
  88. package/package.json +14 -2
  89. package/schemas/workflow-evidence.schema.json +2 -1
  90. package/scripts/hooks/stop-goal-fit.js +66 -18
  91. package/src/cli/workflow-sidecar.ts +101 -21
  92. package/src/flow-kit/validate.ts +55 -1
  93. package/src/index.ts +53 -0
  94. package/src/tools/build-universal-bundles.ts +14 -0
  95. package/tsconfig.json +1 -0
@@ -164,7 +164,7 @@ run_inspect "$ROOT/kits/builder" "$out" || true
164
164
  if node -e "
165
165
  const d = require('fs').readFileSync('$out', 'utf8');
166
166
  const r = JSON.parse(d);
167
- const required = ['kit_id','kit_name','conformance','targets','third_party_extensions'];
167
+ const required = ['kit_id','kit_name','conformance','targets','third_party_extensions','trust'];
168
168
  for (const k of required) {
169
169
  if (!(k in r)) throw new Error('missing key: ' + k);
170
170
  }
@@ -199,6 +199,60 @@ else
199
199
  cat "$out"
200
200
  fi
201
201
 
202
+ # ===================================================================
203
+ echo ""
204
+ echo "=== 8. Trust axis: first-party allowlist (builder and knowledge) ==="
205
+ # ===================================================================
206
+
207
+ for kit_name in builder knowledge; do
208
+ out="$TMP_DIR/trust-${kit_name}.out"
209
+ run_inspect "$ROOT/kits/$kit_name" "$out" || true
210
+ trust=$(node -e "const d=require('fs').readFileSync('$out','utf8'); console.log(JSON.parse(d).trust)" 2>/dev/null)
211
+ if [[ "$trust" == "first-party" ]]; then
212
+ pass "$kit_name kit trust: first-party (in Kontour allowlist)"
213
+ else
214
+ fail "$kit_name kit trust: expected first-party, got '$trust'"
215
+ cat "$out"
216
+ fi
217
+ done
218
+
219
+ # ===================================================================
220
+ echo ""
221
+ echo "=== 9. Trust axis: unverified for third-party and fixture kits ==="
222
+ # ===================================================================
223
+
224
+ for fixture in k0-flows-only k1-agent-extension k2-with-evals third-party-extension; do
225
+ out="$TMP_DIR/trust-${fixture}.out"
226
+ run_inspect "$ROOT/evals/fixtures/kit-conformance-levels/$fixture" "$out" || true
227
+ trust=$(node -e "const d=require('fs').readFileSync('$out','utf8'); console.log(JSON.parse(d).trust)" 2>/dev/null)
228
+ if [[ "$trust" == "unverified" ]]; then
229
+ pass "$fixture fixture trust: unverified (not in first-party allowlist)"
230
+ else
231
+ fail "$fixture fixture trust: expected unverified, got '$trust'"
232
+ cat "$out"
233
+ fi
234
+ done
235
+
236
+ # ===================================================================
237
+ echo ""
238
+ echo "=== 10. Trust field present in inspect JSON schema ==="
239
+ # ===================================================================
240
+
241
+ out="$TMP_DIR/trust-schema.out"
242
+ run_inspect "$ROOT/kits/builder" "$out" || true
243
+ if node -e "
244
+ const d = require('fs').readFileSync('$out', 'utf8');
245
+ const r = JSON.parse(d);
246
+ if (!('trust' in r)) throw new Error('missing key: trust');
247
+ const valid = ['first-party', 'verified', 'unverified'];
248
+ if (!valid.includes(r.trust)) throw new Error('trust must be one of: ' + valid.join(', ') + '; got: ' + r.trust);
249
+ " 2>/dev/null; then
250
+ pass "inspect JSON output includes trust field with valid value"
251
+ else
252
+ fail "inspect JSON output is missing trust field or has invalid value"
253
+ cat "$out"
254
+ fi
255
+
202
256
  # ===================================================================
203
257
  echo ""
204
258
  if [[ "$errors" -eq 0 ]]; then
@@ -544,22 +544,22 @@ else
544
544
  _fail "existing invalid acceptance ref rejection was not fail-closed: $(cat "$TMPDIR_EVAL/invalid-acceptance-ref.out" "$TMPDIR_EVAL/invalid-acceptance-ref.err")"
545
545
  fi
546
546
 
547
- SURFACE_CHECK='{"id":"surface-trust-fixture","kind":"policy","status":"pass","summary":"Surface trust evidence passed.","surface_trust_refs":[{"artifact_kind":"TrustReport","artifact_ref":"trust/report.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Issued during this workflow."},"authority":{"producer":"surface-local","summary":"Local Surface trust producer."},"integrity":{"status":"matched","summary":"Artifact digest matched expected subject and gate.","digest":"sha256:abc123"},"status":"pass","summary":"Accepted Surface claim."}]}'
547
+ SURFACE_CHECK='{"id":"surface-trust-fixture","kind":"policy","status":"pass","summary":"Hachure trust.bundle evidence passed.","surface_trust_refs":[{"artifact_kind":"trust.bundle","artifact_ref":"trust/report.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Issued during this workflow."},"authority":{"producer":"surface-local","summary":"Local Surface trust producer."},"integrity":{"status":"matched","summary":"Artifact digest matched expected subject and gate.","digest":"sha256:abc123"},"status":"pass","summary":"Accepted trust.bundle claim."}]}'
548
548
  if flow_agents_node "$WRITER" record-evidence "$ARTIFACT_DIR" \
549
549
  --verdict pass \
550
550
  --check-json "$SURFACE_CHECK" \
551
551
  --timestamp "2026-05-09T00:01:05Z" >"$TMPDIR_EVAL/surface-evidence.out" 2>"$TMPDIR_EVAL/surface-evidence.err" \
552
552
  && rg -q '"surface_trust_refs"' "$ARTIFACT_DIR/evidence.json" \
553
- && rg -q '"artifact_kind": "TrustReport"' "$ARTIFACT_DIR/evidence.json" \
553
+ && rg -q '"artifact_kind": "trust.bundle"' "$ARTIFACT_DIR/evidence.json" \
554
554
  && ! rg -q 'veritas' "$ARTIFACT_DIR/evidence.json"; then
555
- _pass "sidecar writer records provider-neutral Surface trust refs"
555
+ _pass "sidecar writer records Hachure-aligned trust.bundle refs"
556
556
  else
557
- _fail "sidecar writer did not record Surface trust refs: $(cat "$TMPDIR_EVAL/surface-evidence.out" "$TMPDIR_EVAL/surface-evidence.err")"
557
+ _fail "sidecar writer did not record Hachure-aligned trust.bundle refs: $(cat "$TMPDIR_EVAL/surface-evidence.out" "$TMPDIR_EVAL/surface-evidence.err")"
558
558
  fi
559
559
 
560
560
  if flow_agents_node "$WRITER" record-evidence "$ARTIFACT_DIR" \
561
561
  --verdict pass \
562
- --check-json '{"id":"surface-trust-native-field","kind":"policy","status":"pass","summary":"Should fail.","surface_trust_refs":[{"artifact_kind":"Trust Snapshot","artifact_ref":"trust/snapshot.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists.","veritas_policy":"native-field"},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}]}' >"$TMPDIR_EVAL/surface-invalid.out" 2>&1; then
562
+ --check-json '{"id":"surface-trust-native-field","kind":"policy","status":"pass","summary":"Should fail.","surface_trust_refs":[{"artifact_kind":"trust.bundle","artifact_ref":"trust/snapshot.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists.","veritas_policy":"native-field"},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}]}' >"$TMPDIR_EVAL/surface-invalid.out" 2>&1; then
563
563
  _fail "sidecar writer should reject provider-specific Surface trust fields"
564
564
  elif rg -q 'unsupported field' "$TMPDIR_EVAL/surface-invalid.out"; then
565
565
  _pass "sidecar writer rejects provider-specific Surface trust fields"
@@ -581,10 +581,10 @@ check_contradictory_surface_ref() {
581
581
  fi
582
582
  }
583
583
 
584
- check_contradictory_surface_ref "rejected-pass" '{"artifact_kind":"TrustReport","artifact_ref":"trust/report.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"rejected","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
585
- check_contradictory_surface_ref "stale-pass" '{"artifact_kind":"TrustReport","artifact_ref":"trust/report.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"stale","summary":"Stale."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
586
- check_contradictory_surface_ref "missing-authority-pass" '{"artifact_kind":"TrustReport","artifact_ref":"trust/report.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"unknown","summary":"Producer missing."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
587
- check_contradictory_surface_ref "integrity-mismatch-pass" '{"artifact_kind":"TrustReport","artifact_ref":"trust/report.json","gate_id":"builder.surface.claim","claim_type":"surface.claim","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"mismatch","summary":"Mismatch."},"status":"pass"}'
584
+ check_contradictory_surface_ref "rejected-pass" '{"artifact_kind":"trust.bundle","artifact_ref":"trust/report.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"rejected","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
585
+ check_contradictory_surface_ref "stale-pass" '{"artifact_kind":"trust.bundle","artifact_ref":"trust/report.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"stale","summary":"Stale."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
586
+ check_contradictory_surface_ref "missing-authority-pass" '{"artifact_kind":"trust.bundle","artifact_ref":"trust/report.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"unknown","summary":"Producer missing."},"integrity":{"status":"matched","summary":"Matched."},"status":"pass"}'
587
+ check_contradictory_surface_ref "integrity-mismatch-pass" '{"artifact_kind":"trust.bundle","artifact_ref":"trust/report.json","gate_id":"builder.trust.bundle","claim_type":"builder.trust.bundle","claim_status":"accepted","subject":"builder-kit","freshness":{"status":"fresh","summary":"Fresh."},"authority":{"producer":"surface-local","summary":"Producer exists."},"integrity":{"status":"mismatch","summary":"Mismatch."},"status":"pass"}'
588
588
 
589
589
  SURFACE_FIXTURE_DIR="$ROOT/evals/fixtures/surface-trust"
590
590
  check_surface_fixture() {
package/evals/run.sh CHANGED
@@ -135,6 +135,8 @@ run_static() {
135
135
  echo ""
136
136
  bash "$EVAL_DIR/static/test_evidence_refs.sh" || result=1
137
137
  echo ""
138
+ bash "$EVAL_DIR/static/test_library_exports.sh" || result=1
139
+ echo ""
138
140
  bash "$EVAL_DIR/static/test_console_presets.sh" || result=1
139
141
  echo ""
140
142
  bash "$EVAL_DIR/static/test_repo_hooks.sh" || result=1
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env bash
2
+ # test_library_exports.sh — the package exposes the canonical workflow-sidecar
3
+ # writer/validator as an importable library (issue #99). Guards three things:
4
+ # 1. package.json declares the library entry points (exports/main/types).
5
+ # 2. importing the entry point does NOT execute the CLI (entry guard holds).
6
+ # 3. the CLI still runs when invoked directly (entry guard regression).
7
+ set -uo pipefail
8
+
9
+ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
10
+ source "$ROOT/evals/lib/node.sh"
11
+ cd "$ROOT"
12
+
13
+ errors=0
14
+ pass() { echo " ✓ $1"; }
15
+ fail() { echo " ✗ $1"; errors=$((errors + 1)); }
16
+
17
+ echo "=== Library Export Surface (#99) ==="
18
+
19
+ # Ensure the build exists (cheap no-op if already built).
20
+ flow_agents_node node_modules/typescript/bin/tsc -p tsconfig.json >/dev/null 2>&1 || npm run build --silent >/dev/null 2>&1 || true
21
+
22
+ # 1. package.json entry points
23
+ if node -e '
24
+ const p = require("./package.json");
25
+ const fail = (m) => { console.error(m); process.exit(1); };
26
+ if (p.main !== "build/src/index.js") fail("main must be build/src/index.js");
27
+ if (p.types !== "build/src/index.d.ts") fail("types must be build/src/index.d.ts");
28
+ if (!p.exports || !p.exports["."]) fail("exports must define the root entry");
29
+ const root = p.exports["."];
30
+ if (root.import !== "./build/src/index.js") fail("exports[.].import must be ./build/src/index.js");
31
+ if (root.types !== "./build/src/index.d.ts") fail("exports[.].types must be ./build/src/index.d.ts");
32
+ ' 2>/tmp/lib-exports-pkg.err; then
33
+ pass "package.json declares library entry points (main/types/exports)"
34
+ else
35
+ fail "package.json library entry points missing or wrong: $(cat /tmp/lib-exports-pkg.err)"
36
+ fi
37
+
38
+ # 2. built artifacts present
39
+ if [[ -f "build/src/index.js" && -f "build/src/index.d.ts" ]]; then
40
+ pass "build emits index.js and index.d.ts"
41
+ else
42
+ fail "build is missing index.js or index.d.ts (run npm run build)"
43
+ fi
44
+
45
+ # 3. importing the library does not run the CLI, and the public API is present.
46
+ # If importing executed the CLI it would call process.exit before our marker prints.
47
+ if node --input-type=module -e '
48
+ import * as lib from "./build/src/index.js";
49
+ const required = [
50
+ "validateTrustBundle", "normalizeCheck", "normalizeFinding", "normalizeLearning",
51
+ "normalizeEvidenceRefs", "validateEvidenceRef", "validateLearningCorrection",
52
+ "loadJson", "writeJson", "appendJsonl", "sidecarBase", "writeState",
53
+ "readSidecar", "writeSidecar",
54
+ "statuses", "phases", "checkKinds", "checkStatuses", "verdicts",
55
+ ];
56
+ const missing = required.filter((name) => lib[name] === undefined);
57
+ if (missing.length) { console.error("missing exports: " + missing.join(", ")); process.exit(1); }
58
+ // Exercise a validator to prove it is the real implementation, not a stub.
59
+ let threw = false;
60
+ try { lib.normalizeCheck({ id: "x" }); } catch { threw = true; }
61
+ if (!threw) { console.error("normalizeCheck should reject an invalid check"); process.exit(1); }
62
+ const ok = lib.normalizeCheck({ id: "b", kind: "test", status: "pass", summary: "ok" });
63
+ if (ok.id !== "b") { console.error("normalizeCheck should return the normalized check"); process.exit(1); }
64
+ console.log("LIBRARY_IMPORT_OK");
65
+ ' 2>/dev/null | grep -q "LIBRARY_IMPORT_OK"; then
66
+ pass "importing the library exposes the public API without running the CLI"
67
+ else
68
+ fail "library import failed, ran the CLI, or is missing public exports"
69
+ fi
70
+
71
+ # 4. the CLI still runs when invoked directly (entry guard regression guard).
72
+ # A missing required flag must produce the CLI's own validation error, proving main() ran.
73
+ cli_out="$(node build/src/cli/workflow-sidecar.js ensure-session --artifact-root /tmp/nonexistent-lib-test 2>&1 || true)"
74
+ if echo "$cli_out" | grep -q "task-slug is required"; then
75
+ pass "CLI entry still executes when run directly"
76
+ else
77
+ fail "CLI entry did not run as a script (entry guard regression): $cli_out"
78
+ fi
79
+
80
+ echo ""
81
+ if [[ "$errors" -gt 0 ]]; then
82
+ echo "Library export checks failed: $errors issue(s)."
83
+ exit 1
84
+ fi
85
+ echo "Library export checks passed."
@@ -211,8 +211,8 @@ for (const gateId of ["verify-gate", "merge-ready-gate"]) {
211
211
  const expectations = Object.values(flow.gates || {}).flatMap((gate) => gate.expects || []);
212
212
  if (!expectations.length) throw new Error("Builder build flow should declare gate expectations");
213
213
  for (const expectation of expectations) {
214
- if (expectation.kind !== "surface.claim") throw new Error(`${expectation.id || "<unknown>"} should remain a surface.claim expectation`);
215
- if (!expectation.claim?.type || !expectation.claim?.accepted_statuses) throw new Error(`${expectation.id || "<unknown>"} should declare claim type and accepted statuses`);
214
+ if (expectation.kind !== "trust.bundle") throw new Error(`${expectation.id || "<unknown>"} should remain a trust.bundle expectation`);
215
+ if (!expectation.bundle_claim?.claimType || !expectation.bundle_claim?.accepted_statuses) throw new Error(`${expectation.id || "<unknown>"} should declare bundle_claim claimType and accepted statuses`);
216
216
  }
217
217
  const flowText = JSON.stringify(flow).toLowerCase();
218
218
  for (const term of ["veritas", "trust_provider", "trust-provider", "provider_name", "provider_ref", "veritas_policy", "veritas_readiness"]) {
@@ -221,7 +221,7 @@ for (const term of ["veritas", "trust_provider", "trust-provider", "provider_nam
221
221
  console.log("ok");
222
222
  NODE
223
223
  then
224
- _pass "installed Builder Kit build flow keeps provider-neutral surface.claim gates"
224
+ _pass "installed Builder Kit build flow keeps provider-neutral trust.bundle gates"
225
225
  else
226
226
  _fail "installed Builder Kit build flow route-back or provider-neutral gate policy missing or wrong"
227
227
  fi
@@ -411,6 +411,21 @@ else
411
411
  _fail "catalog metadata check failed"
412
412
  fi
413
413
 
414
+ # Block Reason Channel (#100): the generated opencode/pi adapters must carry the
415
+ # policy reason into their block path so the model learns why it was blocked.
416
+ # claude/codex deny translation is covered in test_hook_category_behaviors.sh.
417
+ BUILDER_SRC="$ROOT_DIR/src/tools/build-universal-bundles.ts"
418
+ if grep -q "throw new Error(policyResult.reason" "$BUILDER_SRC"; then
419
+ _pass "opencode adapter surfaces the block reason to the model (thrown error)"
420
+ else
421
+ _fail "opencode adapter block path dropped the policy reason"
422
+ fi
423
+ if grep -q "reason: result.reason" "$BUILDER_SRC"; then
424
+ _pass "pi adapter surfaces the block reason to the model (block result reason)"
425
+ else
426
+ _fail "pi adapter block path dropped the policy reason"
427
+ fi
428
+
414
429
  echo ""
415
430
  echo "==========================="
416
431
  total=$((pass + fail))
@@ -1075,17 +1075,17 @@ const flow = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
1075
1075
  const expectations = Object.values(flow.gates || {}).flatMap((gate) => gate.expects || []);
1076
1076
  if (!expectations.length) throw new Error("no Builder Kit gate expectations found");
1077
1077
  for (const expectation of expectations) {
1078
- if (expectation.kind !== "surface.claim") throw new Error(`${expectation.id || "<unknown>"} is not a surface.claim expectation`);
1079
- if (!expectation.claim?.type || !expectation.claim?.accepted_statuses) throw new Error(`${expectation.id || "<unknown>"} is missing claim type or accepted statuses`);
1078
+ if (expectation.kind !== "trust.bundle") throw new Error(`${expectation.id || "<unknown>"} is not a trust.bundle expectation`);
1079
+ if (!expectation.bundle_claim?.claimType || !expectation.bundle_claim?.accepted_statuses) throw new Error(`${expectation.id || "<unknown>"} is missing bundle_claim claimType or accepted statuses`);
1080
1080
  }
1081
1081
  const flowText = JSON.stringify(flow).toLowerCase();
1082
1082
  for (const term of ["veritas", "trust_provider", "trust-provider", "provider_name", "provider_ref", "veritas_policy", "veritas_readiness"]) {
1083
1083
  if (flowText.includes(term)) throw new Error(`provider-specific trust field leaked into Builder Kit build flow: ${term}`);
1084
1084
  }
1085
1085
  NODE
1086
- pass "Builder build flow keeps provider-neutral surface.claim expectations"
1086
+ pass "Builder build flow keeps provider-neutral trust.bundle expectations"
1087
1087
  else
1088
- fail "Builder build flow keeps provider-neutral surface.claim expectations"
1088
+ fail "Builder build flow keeps provider-neutral trust.bundle expectations"
1089
1089
  fi
1090
1090
  require_text "$MAP" 'pull-work' "map includes pull-work"
1091
1091
  require_text "$MAP" 'pickup Probe before planning' "map documents pickup Probe before planning"
@@ -19,12 +19,12 @@
19
19
  "expects": [
20
20
  {
21
21
  "id": "selected-work",
22
- "kind": "surface.claim",
22
+ "kind": "trust.bundle",
23
23
  "required": true,
24
24
  "description": "A ready work item is selected with scope and acceptance context.",
25
- "claim": {
26
- "type": "builder.pull-work.selected",
27
- "subject": "work-item",
25
+ "bundle_claim": {
26
+ "claimType": "builder.pull-work.selected",
27
+ "subjectType": "work-item",
28
28
  "accepted_statuses": ["trusted", "accepted"]
29
29
  }
30
30
  }
@@ -35,23 +35,23 @@
35
35
  "expects": [
36
36
  {
37
37
  "id": "pickup-probe-readiness",
38
- "kind": "surface.claim",
38
+ "kind": "trust.bundle",
39
39
  "required": true,
40
40
  "description": "Pickup Probe evidence is recorded before planning: goal fit and scope, blockers and dependencies, dependency freshness, acceptance criteria quality, provider state, risk, stop-short risks, planning readiness, decisions, unresolved questions, accepted gaps, probe_status, probe_artifact_ref, grouping decision, expected modified files, and conflict risks.",
41
- "claim": {
42
- "type": "builder.design-probe.pickup-readiness",
43
- "subject": "work-item",
41
+ "bundle_claim": {
42
+ "claimType": "builder.design-probe.pickup-readiness",
43
+ "subjectType": "work-item",
44
44
  "accepted_statuses": ["trusted", "accepted"]
45
45
  }
46
46
  },
47
47
  {
48
48
  "id": "probe-decisions-or-accepted-gaps",
49
- "kind": "surface.claim",
49
+ "kind": "trust.bundle",
50
50
  "required": true,
51
51
  "description": "Planning is blocked until pickup Probe decisions are captured in the pull-work or plan handoff artifact, or unresolved questions are explicitly recorded as accepted gaps. Stale broad continuation language after a merge is not sufficient for planning newly selected work.",
52
- "claim": {
53
- "type": "builder.design-probe.decisions",
54
- "subject": "decision",
52
+ "bundle_claim": {
53
+ "claimType": "builder.design-probe.decisions",
54
+ "subjectType": "decision",
55
55
  "accepted_statuses": ["trusted", "accepted"]
56
56
  }
57
57
  }
@@ -62,12 +62,12 @@
62
62
  "expects": [
63
63
  {
64
64
  "id": "implementation-plan",
65
- "kind": "surface.claim",
65
+ "kind": "trust.bundle",
66
66
  "required": true,
67
67
  "description": "The implementation plan names files, changes, acceptance evidence, and sequencing.",
68
- "claim": {
69
- "type": "builder.plan.implementation",
70
- "subject": "artifact",
68
+ "bundle_claim": {
69
+ "claimType": "builder.plan.implementation",
70
+ "subjectType": "artifact",
71
71
  "accepted_statuses": ["trusted", "accepted"]
72
72
  }
73
73
  }
@@ -78,12 +78,12 @@
78
78
  "expects": [
79
79
  {
80
80
  "id": "implementation-scope",
81
- "kind": "surface.claim",
81
+ "kind": "trust.bundle",
82
82
  "required": true,
83
83
  "description": "The implementation scope records changed files and excludes unrelated work.",
84
- "claim": {
85
- "type": "builder.execute.scope",
86
- "subject": "change",
84
+ "bundle_claim": {
85
+ "claimType": "builder.execute.scope",
86
+ "subjectType": "change",
87
87
  "accepted_statuses": ["trusted", "accepted"]
88
88
  }
89
89
  }
@@ -105,24 +105,24 @@
105
105
  "expects": [
106
106
  {
107
107
  "id": "tests-evidence",
108
- "kind": "surface.claim",
108
+ "kind": "trust.bundle",
109
109
  "required": true,
110
110
  "description": "Relevant tests or checks have evidence tied to the implemented change.",
111
- "claim": {
112
- "type": "builder.verify.tests",
113
- "subject": "flow-step",
111
+ "bundle_claim": {
112
+ "claimType": "builder.verify.tests",
113
+ "subjectType": "flow-step",
114
114
  "accepted_statuses": ["trusted", "accepted"]
115
115
  }
116
116
  },
117
117
  {
118
118
  "id": "policy-compliance",
119
- "kind": "surface.claim",
119
+ "kind": "trust.bundle",
120
120
  "required": false,
121
121
  "description": "Policy compliance evidence is attached when policy gates apply.",
122
122
  "explore_hint": "Use as advisory evidence for scope, security, privacy, release, or repository policy checks.",
123
- "claim": {
124
- "type": "builder.verify.policy-compliance",
125
- "subject": "artifact",
123
+ "bundle_claim": {
124
+ "claimType": "builder.verify.policy-compliance",
125
+ "subjectType": "artifact",
126
126
  "accepted_statuses": ["trusted", "accepted", "advisory"]
127
127
  }
128
128
  }
@@ -144,12 +144,12 @@
144
144
  "expects": [
145
145
  {
146
146
  "id": "merge-readiness",
147
- "kind": "surface.claim",
147
+ "kind": "trust.bundle",
148
148
  "required": true,
149
149
  "description": "The change is ready for merge review based on scope, evidence, and unresolved risks.",
150
- "claim": {
151
- "type": "builder.merge-ready.readiness",
152
- "subject": "change",
150
+ "bundle_claim": {
151
+ "claimType": "builder.merge-ready.readiness",
152
+ "subjectType": "change",
153
153
  "accepted_statuses": ["trusted", "accepted"]
154
154
  }
155
155
  }
@@ -160,12 +160,12 @@
160
160
  "expects": [
161
161
  {
162
162
  "id": "pull-request-opened",
163
- "kind": "surface.claim",
163
+ "kind": "trust.bundle",
164
164
  "required": true,
165
165
  "description": "A pull request exists with linked work, implementation summary, and verification evidence.",
166
- "claim": {
167
- "type": "builder.pr-open.pull-request",
168
- "subject": "pull-request",
166
+ "bundle_claim": {
167
+ "claimType": "builder.pr-open.pull-request",
168
+ "subjectType": "pull-request",
169
169
  "accepted_statuses": ["trusted", "accepted"]
170
170
  }
171
171
  }
@@ -176,12 +176,12 @@
176
176
  "expects": [
177
177
  {
178
178
  "id": "ci-merge-readiness",
179
- "kind": "surface.claim",
179
+ "kind": "trust.bundle",
180
180
  "required": true,
181
181
  "description": "CI and review status support a merge-ready decision.",
182
- "claim": {
183
- "type": "builder.merge-ready-ci.readiness",
184
- "subject": "pull-request",
182
+ "bundle_claim": {
183
+ "claimType": "builder.merge-ready-ci.readiness",
184
+ "subjectType": "pull-request",
185
185
  "accepted_statuses": ["trusted", "accepted"]
186
186
  }
187
187
  }
@@ -192,23 +192,23 @@
192
192
  "expects": [
193
193
  {
194
194
  "id": "decision-evidence",
195
- "kind": "surface.claim",
195
+ "kind": "trust.bundle",
196
196
  "required": true,
197
197
  "description": "Durable decision evidence from the build is recorded.",
198
- "claim": {
199
- "type": "builder.learn.decisions",
200
- "subject": "decision",
198
+ "bundle_claim": {
199
+ "claimType": "builder.learn.decisions",
200
+ "subjectType": "decision",
201
201
  "accepted_statuses": ["trusted", "accepted"]
202
202
  }
203
203
  },
204
204
  {
205
205
  "id": "learning-evidence",
206
- "kind": "surface.claim",
206
+ "kind": "trust.bundle",
207
207
  "required": true,
208
208
  "description": "Learning evidence from delivery is recorded for future work.",
209
- "claim": {
210
- "type": "builder.learn.evidence",
211
- "subject": "release",
209
+ "bundle_claim": {
210
+ "claimType": "builder.learn.evidence",
211
+ "subjectType": "release",
212
212
  "accepted_statuses": ["trusted", "accepted"]
213
213
  }
214
214
  }
@@ -13,79 +13,79 @@
13
13
  "expects": [
14
14
  {
15
15
  "id": "shaped-problem",
16
- "kind": "surface.claim",
16
+ "kind": "trust.bundle",
17
17
  "required": true,
18
18
  "description": "The problem is stated in terms a builder can act on.",
19
- "claim": {
20
- "type": "builder.shape.problem",
21
- "subject": "decision",
19
+ "bundle_claim": {
20
+ "claimType": "builder.shape.problem",
21
+ "subjectType": "decision",
22
22
  "accepted_statuses": ["trusted", "accepted"]
23
23
  }
24
24
  },
25
25
  {
26
26
  "id": "shaped-outcome",
27
- "kind": "surface.claim",
27
+ "kind": "trust.bundle",
28
28
  "required": true,
29
29
  "description": "The intended outcome is explicit enough to evaluate later.",
30
- "claim": {
31
- "type": "builder.shape.outcome",
32
- "subject": "flow-run",
30
+ "bundle_claim": {
31
+ "claimType": "builder.shape.outcome",
32
+ "subjectType": "flow-run",
33
33
  "accepted_statuses": ["trusted", "accepted"]
34
34
  }
35
35
  },
36
36
  {
37
37
  "id": "shaped-constraints",
38
- "kind": "surface.claim",
38
+ "kind": "trust.bundle",
39
39
  "required": true,
40
40
  "description": "Constraints that affect implementation are captured.",
41
- "claim": {
42
- "type": "builder.shape.constraints",
43
- "subject": "decision",
41
+ "bundle_claim": {
42
+ "claimType": "builder.shape.constraints",
43
+ "subjectType": "decision",
44
44
  "accepted_statuses": ["trusted", "accepted"]
45
45
  }
46
46
  },
47
47
  {
48
48
  "id": "shaped-non-goals",
49
- "kind": "surface.claim",
49
+ "kind": "trust.bundle",
50
50
  "required": true,
51
51
  "description": "Non-goals are named so the build stays scoped.",
52
- "claim": {
53
- "type": "builder.shape.non-goals",
54
- "subject": "decision",
52
+ "bundle_claim": {
53
+ "claimType": "builder.shape.non-goals",
54
+ "subjectType": "decision",
55
55
  "accepted_statuses": ["trusted", "accepted"]
56
56
  }
57
57
  },
58
58
  {
59
59
  "id": "shaped-success",
60
- "kind": "surface.claim",
60
+ "kind": "trust.bundle",
61
61
  "required": true,
62
62
  "description": "Success criteria are concrete enough for verification.",
63
- "claim": {
64
- "type": "builder.shape.success",
65
- "subject": "artifact",
63
+ "bundle_claim": {
64
+ "claimType": "builder.shape.success",
65
+ "subjectType": "artifact",
66
66
  "accepted_statuses": ["trusted", "accepted"]
67
67
  }
68
68
  },
69
69
  {
70
70
  "id": "shaped-risk",
71
- "kind": "surface.claim",
71
+ "kind": "trust.bundle",
72
72
  "required": true,
73
73
  "description": "Material delivery risks are recorded before breakdown.",
74
- "claim": {
75
- "type": "builder.shape.risk",
76
- "subject": "decision",
74
+ "bundle_claim": {
75
+ "claimType": "builder.shape.risk",
76
+ "subjectType": "decision",
77
77
  "accepted_statuses": ["trusted", "accepted"]
78
78
  }
79
79
  },
80
80
  {
81
81
  "id": "open-decisions",
82
- "kind": "surface.claim",
82
+ "kind": "trust.bundle",
83
83
  "required": false,
84
84
  "description": "Open decisions are recorded when the shape is not fully settled.",
85
85
  "explore_hint": "Attach when unresolved choices could affect slices or issue boundaries.",
86
- "claim": {
87
- "type": "builder.shape.open-decisions",
88
- "subject": "decision",
86
+ "bundle_claim": {
87
+ "claimType": "builder.shape.open-decisions",
88
+ "subjectType": "decision",
89
89
  "accepted_statuses": ["trusted", "accepted", "advisory"]
90
90
  }
91
91
  }
@@ -96,12 +96,12 @@
96
96
  "expects": [
97
97
  {
98
98
  "id": "slices-defined",
99
- "kind": "surface.claim",
99
+ "kind": "trust.bundle",
100
100
  "required": true,
101
101
  "description": "The shaped work is split into independently useful slices.",
102
- "claim": {
103
- "type": "builder.breakdown.slices",
104
- "subject": "work-item",
102
+ "bundle_claim": {
103
+ "claimType": "builder.breakdown.slices",
104
+ "subjectType": "work-item",
105
105
  "accepted_statuses": ["trusted", "accepted"]
106
106
  }
107
107
  }
@@ -112,12 +112,12 @@
112
112
  "expects": [
113
113
  {
114
114
  "id": "work-items-filed",
115
- "kind": "surface.claim",
115
+ "kind": "trust.bundle",
116
116
  "required": true,
117
117
  "description": "Work items are filed or otherwise recorded with enough context to pull later.",
118
- "claim": {
119
- "type": "builder.file-issues.work-items",
120
- "subject": "work-item",
118
+ "bundle_claim": {
119
+ "claimType": "builder.file-issues.work-items",
120
+ "subjectType": "work-item",
121
121
  "accepted_statuses": ["trusted", "accepted"]
122
122
  }
123
123
  }