@kontourai/flow-agents 2.4.0 → 3.0.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 (89) hide show
  1. package/.github/CODEOWNERS +8 -0
  2. package/.github/workflows/ci.yml +12 -0
  3. package/.github/workflows/trust-reconcile.yml +62 -4
  4. package/CHANGELOG.md +23 -0
  5. package/CONTEXT.md +21 -0
  6. package/build/src/cli/assignment-provider.d.ts +1 -0
  7. package/build/src/cli/assignment-provider.js +748 -0
  8. package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
  9. package/build/src/cli/effective-assignment-provider-settings.js +125 -0
  10. package/build/src/cli/validate-workflow-artifacts.js +5 -1
  11. package/build/src/cli/workflow-sidecar.js +157 -110
  12. package/build/src/cli.js +6 -0
  13. package/build/src/tools/validate-source-tree.js +1 -0
  14. package/context/contracts/artifact-contract.md +2 -0
  15. package/context/contracts/assignment-provider-contract.md +239 -0
  16. package/context/contracts/builder-kit-workflow-state-contract.md +2 -0
  17. package/context/contracts/decision-registry-contract.md +2 -0
  18. package/context/contracts/delivery-contract.md +2 -0
  19. package/context/contracts/execution-contract.md +25 -0
  20. package/context/contracts/governance-adapter-contract.md +2 -0
  21. package/context/contracts/knowledge-store-contract.md +197 -0
  22. package/context/contracts/planning-contract.md +2 -0
  23. package/context/contracts/review-contract.md +2 -0
  24. package/context/contracts/sandbox-policy.md +2 -0
  25. package/context/contracts/standing-directives.md +13 -0
  26. package/context/contracts/verification-contract.md +2 -0
  27. package/context/contracts/work-item-contract.md +2 -0
  28. package/context/settings/assignment-provider-settings.json +33 -0
  29. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +180 -0
  30. package/docs/context-map.md +1 -0
  31. package/docs/decisions/index.md +3 -0
  32. package/docs/decisions/knowledge-store-provider.md +51 -0
  33. package/docs/decisions/model-routing.md +63 -0
  34. package/docs/decisions/standing-directives.md +66 -0
  35. package/docs/fixture-ownership.md +1 -0
  36. package/docs/workflow-shared-contracts.md +2 -1
  37. package/docs/workflow-usage-guide.md +6 -0
  38. package/evals/ci/run-baseline.sh +6 -0
  39. package/evals/fixtures/assignment-provider/actor-a.json +6 -0
  40. package/evals/fixtures/assignment-provider/actor-b.json +6 -0
  41. package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
  42. package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
  43. package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
  44. package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
  45. package/evals/integration/test_assignment_provider_github.sh +318 -0
  46. package/evals/integration/test_assignment_provider_local_file.sh +222 -0
  47. package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
  48. package/evals/integration/test_fixture_retirement_audit.sh +2 -2
  49. package/evals/integration/test_publish_delivery.sh +21 -4
  50. package/evals/integration/test_pull_work_assignment_join.sh +132 -0
  51. package/evals/integration/test_pull_work_liveness_preflight.sh +14 -6
  52. package/evals/integration/test_reconcile_soundness.sh +33 -9
  53. package/evals/integration/test_trust_reconcile.sh +9 -8
  54. package/evals/integration/test_trust_reconcile_negatives.sh +608 -0
  55. package/evals/integration/test_workflow_sidecar_writer.sh +79 -0
  56. package/evals/run.sh +10 -0
  57. package/evals/static/test_flowdef_codeowners_coverage.sh +6 -0
  58. package/evals/static/test_knowledge_providers.sh +23 -0
  59. package/kits/builder/skills/deliver/SKILL.md +19 -0
  60. package/kits/builder/skills/pull-work/SKILL.md +78 -10
  61. package/kits/knowledge/kit.json +35 -0
  62. package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
  63. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
  64. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
  65. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
  66. package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
  67. package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
  68. package/kits/knowledge/providers/conformance/suite.test.js +125 -0
  69. package/kits/knowledge/providers/git-repo/index.js +236 -0
  70. package/kits/knowledge/providers/health/health-pass.test.js +99 -0
  71. package/kits/knowledge/providers/health/index.js +153 -0
  72. package/kits/knowledge/providers/index.js +24 -0
  73. package/kits/knowledge/providers/lib/model.js +91 -0
  74. package/kits/knowledge/providers/lib/schema-validate.js +119 -0
  75. package/kits/knowledge/providers/markdown-vault/index.js +169 -0
  76. package/kits/knowledge/providers/work-item/index.js +204 -0
  77. package/package.json +4 -2
  78. package/schemas/assignment-provider-settings.schema.json +125 -0
  79. package/schemas/knowledge/edge.schema.json +54 -0
  80. package/schemas/knowledge/health-report.schema.json +45 -0
  81. package/schemas/knowledge/node.schema.json +49 -0
  82. package/schemas/knowledge/proposal.schema.json +53 -0
  83. package/scripts/ci/trust-reconcile.js +521 -24
  84. package/src/cli/assignment-provider.ts +845 -0
  85. package/src/cli/effective-assignment-provider-settings.ts +112 -0
  86. package/src/cli/validate-workflow-artifacts.ts +5 -1
  87. package/src/cli/workflow-sidecar.ts +147 -106
  88. package/src/cli.ts +6 -0
  89. package/src/tools/validate-source-tree.ts +1 -0
@@ -23,6 +23,14 @@
23
23
  # every honest human-attestation use) BUT MUST be loudly, distinctly marked
24
24
  # 'ATTESTED (not independently verifiable at L0)' plus the summary count line — never a
25
25
  # quiet SESSION-LOCAL OK indistinguishable from a reconciled check. See ADR 0020 Residuals.
26
+ # 7. delivery/DECLARED marker regressions (ADR 0022 §1/§2, section 7 below) — bundle-absent
27
+ # fail-closed default, malformed/empty-array/missing-field diagnostics, and all four
28
+ # matchesScope() forms (ref:, commit: single + range, author:, branch-prefix:) including
29
+ # positive AND near-miss coverage, plus the compound (space-separated AND) scope form a
30
+ # security review added: ref:/branch-prefix: alone match GITHUB_HEAD_REF, which is
31
+ # PUSHER-CONTROLLED on a fork PR, so an identity-binding scope MUST combine author: with
32
+ # a second condition — 7l/7m prove both-match exempts and only-one-match still fails
33
+ # closed.
26
34
  #
27
35
  # Fresh-verify (Step 1) is a trivial pass so the proof is focused on the reconcile step; the
28
36
  # manifest resolves from this repo's live run-baseline.sh registry (--repo-root "$ROOT").
@@ -141,6 +149,606 @@ else
141
149
  fi
142
150
  fi
143
151
 
152
+ # 7. DECLARED-marker (no-agent-delivery) regressions — ADR 0022 §1/§2.
153
+ # Bundle-required-by-default: when no delivery/trust.bundle is discovered, the reconciler
154
+ # fails closed unless a well-formed, in-scope delivery/DECLARED marker exempts Step 2.
155
+ # Fresh verify (Step 1) is a trivial passing command (TRUST_RECONCILE_COMMANDS) so each
156
+ # case below isolates the new bundle-absence branch. No --bundle argument is used — these
157
+ # are repo-root-only cases (mktemp -d per case, mirroring test_trust_reconcile.sh's
158
+ # pattern since run_case()/_bundle_ style fixtures do not apply without a bundle).
159
+ echo ""
160
+ echo "=== 7. delivery/DECLARED marker regressions (ADR 0022 §1/§2) ==="
161
+
162
+ DECLARED_TMPROOT="$(mktemp -d)"
163
+ cleanup_declared() { rm -rf "$DECLARED_TMPROOT"; }
164
+ trap cleanup_declared EXIT
165
+
166
+ DECLARED_CMD="node -e 'process.exit(0)'"
167
+
168
+ # write_declared <dir> <json>
169
+ # Writes <json> verbatim to <dir>/delivery/DECLARED (creating delivery/ if needed).
170
+ write_declared() {
171
+ local dir="$1" json="$2"
172
+ mkdir -p "$dir/delivery"
173
+ printf '%s' "$json" > "$dir/delivery/DECLARED"
174
+ }
175
+
176
+ # 7a. No bundle, no delivery/DECLARED at all → fail closed, bundle-required-no-declared-marker.
177
+ CASE7A="$DECLARED_TMPROOT/no-marker"
178
+ mkdir -p "$CASE7A"
179
+ out7a="$(TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
180
+ node "$RECONCILE" --repo-root "$CASE7A" 2>&1)"
181
+ code7a=$?
182
+ if [[ $code7a -ne 0 ]]; then
183
+ _pass "no-bundle-no-marker: reconciler exits non-zero ($code7a)"
184
+ else
185
+ _fail "no-bundle-no-marker: expected non-zero exit, got 0 — output: $out7a"
186
+ fi
187
+ if echo "$out7a" | grep -qF "bundle-required-no-declared-marker"; then
188
+ _pass "no-bundle-no-marker: emitted 'bundle-required-no-declared-marker'"
189
+ else
190
+ _fail "no-bundle-no-marker: expected 'bundle-required-no-declared-marker' — output: $out7a"
191
+ fi
192
+ if echo "$out7a" | grep -qF "no delivery/DECLARED marker found"; then
193
+ _pass "no-bundle-no-marker: emitted 'no delivery/DECLARED marker found'"
194
+ else
195
+ _fail "no-bundle-no-marker: expected 'no delivery/DECLARED marker found' — output: $out7a"
196
+ fi
197
+
198
+ # 7b. delivery/DECLARED missing approved_by → treated as absent, missing field named.
199
+ CASE7B="$DECLARED_TMPROOT/missing-field"
200
+ mkdir -p "$CASE7B"
201
+ write_declared "$CASE7B" '{"scope":"ref:feature/foo","reason":"human maintainer PR","declared_at":"2026-07-01T00:00:00Z"}'
202
+ out7b="$(TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
203
+ node "$RECONCILE" --repo-root "$CASE7B" 2>&1)"
204
+ code7b=$?
205
+ if [[ $code7b -ne 0 ]]; then
206
+ _pass "missing-field: reconciler exits non-zero ($code7b)"
207
+ else
208
+ _fail "missing-field: expected non-zero exit, got 0 — output: $out7b"
209
+ fi
210
+ if echo "$out7b" | grep -qF "approved_by"; then
211
+ _pass "missing-field: output names the missing field 'approved_by'"
212
+ else
213
+ _fail "missing-field: expected 'approved_by' in output — output: $out7b"
214
+ fi
215
+ if echo "$out7b" | grep -qF "missing required field"; then
216
+ _pass "missing-field: output contains 'missing required field'"
217
+ else
218
+ _fail "missing-field: expected 'missing required field' — output: $out7b"
219
+ fi
220
+
221
+ # 7c. delivery/DECLARED malformed JSON (truncated) → not valid JSON.
222
+ CASE7C="$DECLARED_TMPROOT/malformed-json"
223
+ mkdir -p "$CASE7C"
224
+ write_declared "$CASE7C" '{"scope":"ref:feature/foo","reason":"human maintainer PR"'
225
+ out7c="$(TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
226
+ node "$RECONCILE" --repo-root "$CASE7C" 2>&1)"
227
+ code7c=$?
228
+ if [[ $code7c -ne 0 ]]; then
229
+ _pass "malformed-json: reconciler exits non-zero ($code7c)"
230
+ else
231
+ _fail "malformed-json: expected non-zero exit, got 0 — output: $out7c"
232
+ fi
233
+ if echo "$out7c" | grep -qF "not valid JSON"; then
234
+ _pass "malformed-json: output contains 'not valid JSON'"
235
+ else
236
+ _fail "malformed-json: expected 'not valid JSON' — output: $out7c"
237
+ fi
238
+
239
+ # 7d. Well-formed ref: marker matching TRUST_RECONCILE_REF → exempt, exact DECLARED line,
240
+ # and Step 1 (fresh verify) still ran (canonical command string appears in stdout).
241
+ CASE7D="$DECLARED_TMPROOT/ref-match"
242
+ mkdir -p "$CASE7D"
243
+ write_declared "$CASE7D" '{"scope":"ref:feature/foo","reason":"human maintainer PR","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
244
+ out7d="$(TRUST_RECONCILE_REF="feature/foo" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
245
+ node "$RECONCILE" --repo-root "$CASE7D" 2>&1)"
246
+ code7d=$?
247
+ if [[ $code7d -eq 0 ]]; then
248
+ _pass "ref-match: reconciler exits 0 (in-scope ref: marker exempts Step 2)"
249
+ else
250
+ _fail "ref-match: expected exit 0, got $code7d — output: $out7d"
251
+ fi
252
+ if echo "$out7d" | grep -qF "DECLARED (no-agent-delivery): ref:feature/foo — human maintainer PR (approved by alice, declared 2026-07-01T00:00:00Z)"; then
253
+ _pass "ref-match: emitted the exact DECLARED line"
254
+ else
255
+ _fail "ref-match: expected the exact DECLARED line — output: $out7d"
256
+ fi
257
+ if echo "$out7d" | grep -qF "$DECLARED_CMD"; then
258
+ _pass "ref-match: canonical command still ran (Step 1 not skipped)"
259
+ else
260
+ _fail "ref-match: expected the canonical command '$DECLARED_CMD' to appear in output (Step 1 must still run) — output: $out7d"
261
+ fi
262
+
263
+ # 7e. Well-formed author: marker NOT matching TRUST_RECONCILE_ACTOR (near-miss) → out of
264
+ # scope, proves no accidental wildcard match.
265
+ CASE7E="$DECLARED_TMPROOT/author-near-miss"
266
+ mkdir -p "$CASE7E"
267
+ write_declared "$CASE7E" '{"scope":"author:dependabot[bot]","reason":"dependabot dependency updates","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
268
+ out7e="$(TRUST_RECONCILE_ACTOR="not-dependabot" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
269
+ node "$RECONCILE" --repo-root "$CASE7E" 2>&1)"
270
+ code7e=$?
271
+ if [[ $code7e -ne 0 ]]; then
272
+ _pass "author-near-miss: reconciler exits non-zero ($code7e) — near-miss actor does not match"
273
+ else
274
+ _fail "author-near-miss: expected non-zero exit (out of scope), got 0 — output: $out7e"
275
+ fi
276
+ if echo "$out7e" | grep -qF "out of scope"; then
277
+ _pass "author-near-miss: output contains 'out of scope'"
278
+ else
279
+ _fail "author-near-miss: expected 'out of scope' — output: $out7e"
280
+ fi
281
+
282
+ # 7f. Well-formed branch-prefix:dependabot/ marker matching TRUST_RECONCILE_REF → exempt.
283
+ CASE7F="$DECLARED_TMPROOT/branch-prefix-match"
284
+ mkdir -p "$CASE7F"
285
+ write_declared "$CASE7F" '{"scope":"branch-prefix:dependabot/","reason":"dependabot dependency updates","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
286
+ out7f="$(TRUST_RECONCILE_REF="dependabot/npm_and_yarn/foo" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
287
+ node "$RECONCILE" --repo-root "$CASE7F" 2>&1)"
288
+ code7f=$?
289
+ if [[ $code7f -eq 0 ]]; then
290
+ _pass "branch-prefix-match: reconciler exits 0 (in-scope branch-prefix: marker exempts Step 2)"
291
+ else
292
+ _fail "branch-prefix-match: expected exit 0, got $code7f — output: $out7f"
293
+ fi
294
+ if echo "$out7f" | grep -qF "DECLARED (no-agent-delivery):"; then
295
+ _pass "branch-prefix-match: DECLARED line present"
296
+ else
297
+ _fail "branch-prefix-match: expected a DECLARED line — output: $out7f"
298
+ fi
299
+
300
+ # 7g. Array-form delivery/DECLARED with context matching only the second entry → exempt,
301
+ # DECLARED line names the second entry's scope; first entry's non-match is not a failure.
302
+ CASE7G="$DECLARED_TMPROOT/array-form"
303
+ mkdir -p "$CASE7G"
304
+ write_declared "$CASE7G" '[
305
+ {"scope":"author:dependabot[bot]","reason":"dependabot dependency updates","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"},
306
+ {"scope":"ref:release-please--branches--main","reason":"release-please automation PR","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}
307
+ ]'
308
+ out7g="$(TRUST_RECONCILE_REF="release-please--branches--main" TRUST_RECONCILE_ACTOR="not-dependabot" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
309
+ node "$RECONCILE" --repo-root "$CASE7G" 2>&1)"
310
+ code7g=$?
311
+ if [[ $code7g -eq 0 ]]; then
312
+ _pass "array-form: reconciler exits 0 (second array entry matches by ref:)"
313
+ else
314
+ _fail "array-form: expected exit 0, got $code7g — output: $out7g"
315
+ fi
316
+ if echo "$out7g" | grep -qF "DECLARED (no-agent-delivery): ref:release-please--branches--main"; then
317
+ _pass "array-form: DECLARED line names the second entry's scope"
318
+ else
319
+ _fail "array-form: expected DECLARED line naming 'ref:release-please--branches--main' — output: $out7g"
320
+ fi
321
+
322
+ # 7h. Empty-array delivery/DECLARED ([]) → distinct diagnostic, fail closed (MEDIUM-1).
323
+ CASE7H="$DECLARED_TMPROOT/empty-array"
324
+ mkdir -p "$CASE7H"
325
+ write_declared "$CASE7H" '[]'
326
+ out7h="$(TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
327
+ node "$RECONCILE" --repo-root "$CASE7H" 2>&1)"
328
+ code7h=$?
329
+ if [[ $code7h -ne 0 ]]; then
330
+ _pass "empty-array: reconciler exits non-zero ($code7h)"
331
+ else
332
+ _fail "empty-array: expected non-zero exit, got 0 — output: $out7h"
333
+ fi
334
+ if echo "$out7h" | grep -qF "array contains zero entries"; then
335
+ _pass "empty-array: emitted the distinct 'array contains zero entries' diagnostic"
336
+ else
337
+ _fail "empty-array: expected 'array contains zero entries' — output: $out7h"
338
+ fi
339
+
340
+ # 7i. commit:<sha> single-sha exact match (positive coverage, MEDIUM-2).
341
+ CASE7I="$DECLARED_TMPROOT/commit-sha-match"
342
+ mkdir -p "$CASE7I"
343
+ write_declared "$CASE7I" '{"scope":"commit:deadbeef1234","reason":"pinned single commit","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
344
+ out7i="$(TRUST_RECONCILE_SHA="deadbeef1234" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
345
+ node "$RECONCILE" --repo-root "$CASE7I" 2>&1)"
346
+ code7i=$?
347
+ if [[ $code7i -eq 0 ]]; then
348
+ _pass "commit-sha-match: reconciler exits 0 (exact commit: sha match exempts Step 2)"
349
+ else
350
+ _fail "commit-sha-match: expected exit 0, got $code7i — output: $out7i"
351
+ fi
352
+ if echo "$out7i" | grep -qF "DECLARED (no-agent-delivery): commit:deadbeef1234"; then
353
+ _pass "commit-sha-match: DECLARED line present"
354
+ else
355
+ _fail "commit-sha-match: expected a DECLARED line — output: $out7i"
356
+ fi
357
+
358
+ # 7j. commit:<from>..<to> range, unresolvable (repo-root is not a git repo git merge-base
359
+ # can resolve against) → isShaInRange() is best-effort and MUST fail closed, never throw.
360
+ CASE7J="$DECLARED_TMPROOT/commit-range-unresolvable"
361
+ mkdir -p "$CASE7J"
362
+ write_declared "$CASE7J" '{"scope":"commit:aaaaaaaa..bbbbbbbb","reason":"release window","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
363
+ out7j="$(TRUST_RECONCILE_SHA="cccccccc" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
364
+ node "$RECONCILE" --repo-root "$CASE7J" 2>&1)"
365
+ code7j=$?
366
+ if [[ $code7j -ne 0 ]]; then
367
+ _pass "commit-range-unresolvable: reconciler exits non-zero ($code7j) — underivable range never matches"
368
+ else
369
+ _fail "commit-range-unresolvable: expected non-zero exit (fail closed), got 0 — output: $out7j"
370
+ fi
371
+ if echo "$out7j" | grep -qF "out of scope"; then
372
+ _pass "commit-range-unresolvable: output contains 'out of scope'"
373
+ else
374
+ _fail "commit-range-unresolvable: expected 'out of scope' — output: $out7j"
375
+ fi
376
+
377
+ # 7k. author: positive match (in-scope, standalone — 7e above only covers the near-miss).
378
+ CASE7K="$DECLARED_TMPROOT/author-positive-match"
379
+ mkdir -p "$CASE7K"
380
+ write_declared "$CASE7K" '{"scope":"author:dependabot[bot]","reason":"dependabot dependency updates","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
381
+ out7k="$(TRUST_RECONCILE_ACTOR="dependabot[bot]" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
382
+ node "$RECONCILE" --repo-root "$CASE7K" 2>&1)"
383
+ code7k=$?
384
+ if [[ $code7k -eq 0 ]]; then
385
+ _pass "author-positive-match: reconciler exits 0 (exact author: match exempts Step 2)"
386
+ else
387
+ _fail "author-positive-match: expected exit 0, got $code7k — output: $out7k"
388
+ fi
389
+ if echo "$out7k" | grep -qF "DECLARED (no-agent-delivery): author:dependabot[bot]"; then
390
+ _pass "author-positive-match: DECLARED line present"
391
+ else
392
+ _fail "author-positive-match: expected a DECLARED line — output: $out7k"
393
+ fi
394
+
395
+ # 7l/7m. Compound (space-separated AND) scope — HIGH-1 fix + its regression proof.
396
+ # author:<exact> alone (or branch-prefix:<prefix> alone) matches GITHUB_HEAD_REF/GITHUB_ACTOR,
397
+ # but GITHUB_HEAD_REF is PUSHER-CONTROLLED on a fork PR — a ref:/branch-prefix:-only scope
398
+ # can be satisfied by anyone who can name a branch. A compound scope requires ALL
399
+ # space-separated conditions to match before it exempts Step 2.
400
+ CASE7L="$DECLARED_TMPROOT/compound-both-match"
401
+ mkdir -p "$CASE7L"
402
+ write_declared "$CASE7L" '{"scope":"author:github-actions[bot] branch-prefix:release-please--","reason":"release-please automation PR","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
403
+ out7l="$(TRUST_RECONCILE_ACTOR="github-actions[bot]" TRUST_RECONCILE_REF="release-please--branches--main" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
404
+ node "$RECONCILE" --repo-root "$CASE7L" 2>&1)"
405
+ code7l=$?
406
+ if [[ $code7l -eq 0 ]]; then
407
+ _pass "compound-both-match: reconciler exits 0 (both AND conditions match)"
408
+ else
409
+ _fail "compound-both-match: expected exit 0, got $code7l — output: $out7l"
410
+ fi
411
+ if echo "$out7l" | grep -qF "DECLARED (no-agent-delivery): author:github-actions[bot] branch-prefix:release-please--"; then
412
+ _pass "compound-both-match: DECLARED line names the full compound scope"
413
+ else
414
+ _fail "compound-both-match: expected the compound-scope DECLARED line — output: $out7l"
415
+ fi
416
+
417
+ CASE7M="$DECLARED_TMPROOT/compound-only-one-matches"
418
+ mkdir -p "$CASE7M"
419
+ write_declared "$CASE7M" '{"scope":"author:github-actions[bot] branch-prefix:release-please--","reason":"release-please automation PR","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
420
+ out7m="$(TRUST_RECONCILE_REF="release-please--branches--main" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
421
+ node "$RECONCILE" --repo-root "$CASE7M" 2>&1)"
422
+ code7m=$?
423
+ if [[ $code7m -ne 0 ]]; then
424
+ _pass "compound-only-one-matches: reconciler exits non-zero ($code7m) — branch-prefix: alone (no author:) is pusher-controlled and must not exempt"
425
+ else
426
+ _fail "compound-only-one-matches: expected non-zero exit (fail closed — ref alone is insufficient), got 0 — output: $out7m"
427
+ fi
428
+ if echo "$out7m" | grep -qF "out of scope"; then
429
+ _pass "compound-only-one-matches: output contains 'out of scope'"
430
+ else
431
+ _fail "compound-only-one-matches: expected 'out of scope' — output: $out7m"
432
+ fi
433
+
434
+ # 7n/7o/7p. Bundle-ownership staleness check (ADR 0022 addendum, part 2) — live incident PR
435
+ # #278: an AUTO-DISCOVERED bundle must attest THIS change (checkpoint commit_sha equal-to
436
+ # or a git-ancestor-of this change's own sha), or it is treated as ABSENT, not present.
437
+ # Applies only to auto-discovery (no --bundle flag is used in 7n/7o/7p below — that is
438
+ # exactly the point: these are --repo-root-only cases, matching real CI's own invocation
439
+ # shape, which never passes --bundle).
440
+
441
+ STALE_SHA="deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
442
+ OURS_SHA="cafebabecafebabecafebabecafebabecafebabe"
443
+
444
+ # write_stale_checkpoint <dir> <sha>
445
+ # Writes a minimal, well-formed delivery/trust.checkpoint.json envelope naming <sha> as
446
+ # commit_sha -- auto-discovered by trust-reconcile.js the same way a real sealed checkpoint
447
+ # is (discoverBundle() checks the full bundle first, then this file).
448
+ write_stale_checkpoint() {
449
+ local dir="$1" sha="$2"
450
+ mkdir -p "$dir/delivery"
451
+ printf '{"schema_version":"1.0","slug":"stale-fixture","work_item":null,"status":"delivered","phase":"release","sealed_at":"2026-01-01T00:00:00Z","commit_sha":"%s","checkpoint":{"asOf":"2026-01-01T00:00:00.000Z","statusByClaimId":{}}}' \
452
+ "$sha" > "$dir/delivery/trust.checkpoint.json"
453
+ }
454
+
455
+ # write_fresh_bundle <dir> <label>
456
+ # Writes a minimal, well-formed delivery/trust.bundle with ONE claimed-pass command
457
+ # (<label>) so Step 2's RECONCILED path has something real to reconcile.
458
+ write_fresh_bundle() {
459
+ local dir="$1" label="$2"
460
+ mkdir -p "$dir/delivery"
461
+ cat > "$dir/delivery/trust.bundle" << EOF
462
+ {
463
+ "schemaVersion": 5,
464
+ "source": "test-fixture:fresh-matching-bundle",
465
+ "claims": [
466
+ {
467
+ "id": "c1", "claimType": "workflow.check.build", "value": "pass", "status": "verified",
468
+ "subjectId": "test-slug/build", "facet": "flow-agents.workflow", "subjectType": "workflow-check",
469
+ "fieldOrBehavior": "build", "createdAt": "2026-07-01T00:00:00Z", "updatedAt": "2026-07-01T00:00:00Z",
470
+ "impactLevel": "high", "verificationPolicyId": "policy:workflow.check.build"
471
+ }
472
+ ],
473
+ "evidence": [
474
+ {
475
+ "id": "ev1", "claimId": "c1", "evidenceType": "test_output", "method": "validation",
476
+ "sourceRef": "test-slug/command-log.jsonl", "excerptOrSummary": "build",
477
+ "observedAt": "2026-07-01T00:00:00Z", "collectedBy": "flow-agents/evidence-capture",
478
+ "passing": true,
479
+ "execution": { "runner": "bash", "label": "$label", "isError": false, "exitCode": 0 }
480
+ }
481
+ ],
482
+ "policies": [], "events": []
483
+ }
484
+ EOF
485
+ }
486
+
487
+ # 7n. Stale bundle (checkpoint attesting a DIFFERENT sha) + a valid, in-scope DECLARED
488
+ # marker -> the DECLARED path is taken (not a stale-bundle-triggered fail), and the
489
+ # loud stale-bundle line is still printed for audit.
490
+ CASE7N="$DECLARED_TMPROOT/stale-bundle-with-marker"
491
+ mkdir -p "$CASE7N"
492
+ write_stale_checkpoint "$CASE7N" "$STALE_SHA"
493
+ write_declared "$CASE7N" '{"scope":"ref:feature/y","reason":"human maintainer PR","approved_by":"alice","declared_at":"2026-07-01T00:00:00Z"}'
494
+ out7n="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_REF="feature/y" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
495
+ node "$RECONCILE" --repo-root "$CASE7N" 2>&1)"
496
+ code7n=$?
497
+ if [[ $code7n -eq 0 ]]; then
498
+ _pass "stale-bundle-with-marker: reconciler exits 0 (DECLARED exempts Step 2 after the stale bundle is discarded)"
499
+ else
500
+ _fail "stale-bundle-with-marker: expected exit 0, got $code7n -- output: $out7n"
501
+ fi
502
+ if echo "$out7n" | grep -qF "stale bundle ignored — attests $STALE_SHA, this change is $OURS_SHA"; then
503
+ _pass "stale-bundle-with-marker: emitted the exact stale-bundle line naming both shas"
504
+ else
505
+ _fail "stale-bundle-with-marker: expected the stale-bundle line -- output: $out7n"
506
+ fi
507
+ if echo "$out7n" | grep -qF "DECLARED (no-agent-delivery): ref:feature/y"; then
508
+ _pass "stale-bundle-with-marker: DECLARED line present (marker path taken, not the stale bundle)"
509
+ else
510
+ _fail "stale-bundle-with-marker: expected the DECLARED line -- output: $out7n"
511
+ fi
512
+
513
+ # 7o. Stale bundle + NO delivery/DECLARED marker -> fail closed exactly as bundle-absence.
514
+ CASE7O="$DECLARED_TMPROOT/stale-bundle-no-marker"
515
+ mkdir -p "$CASE7O"
516
+ write_stale_checkpoint "$CASE7O" "$STALE_SHA"
517
+ out7o="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
518
+ node "$RECONCILE" --repo-root "$CASE7O" 2>&1)"
519
+ code7o=$?
520
+ if [[ $code7o -ne 0 ]]; then
521
+ _pass "stale-bundle-no-marker: reconciler exits non-zero ($code7o) -- stale bundle treated as absent, no marker to exempt it"
522
+ else
523
+ _fail "stale-bundle-no-marker: expected non-zero exit, got 0 -- output: $out7o"
524
+ fi
525
+ if echo "$out7o" | grep -qF "stale bundle ignored — attests $STALE_SHA, this change is $OURS_SHA"; then
526
+ _pass "stale-bundle-no-marker: emitted the exact stale-bundle line"
527
+ else
528
+ _fail "stale-bundle-no-marker: expected the stale-bundle line -- output: $out7o"
529
+ fi
530
+ if echo "$out7o" | grep -qF "bundle-required-no-declared-marker"; then
531
+ _pass "stale-bundle-no-marker: emitted 'bundle-required-no-declared-marker' (same fail-closed path as no-bundle-at-all)"
532
+ else
533
+ _fail "stale-bundle-no-marker: expected 'bundle-required-no-declared-marker' -- output: $out7o"
534
+ fi
535
+
536
+ # 7p. REGRESSION GUARD: a fresh/matching auto-discovered bundle (checkpoint commit_sha ==
537
+ # this change's own sha) is NOT treated as stale -- Step 2 reconciles it exactly as
538
+ # before (no behavior change for owned bundles).
539
+ CASE7P="$DECLARED_TMPROOT/fresh-matching-bundle"
540
+ mkdir -p "$CASE7P"
541
+ FRESH_LABEL="node -e 'process.exit(0)'"
542
+ write_fresh_bundle "$CASE7P" "$FRESH_LABEL"
543
+ write_stale_checkpoint "$CASE7P" "$OURS_SHA"
544
+ out7p="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL" \
545
+ node "$RECONCILE" --repo-root "$CASE7P" 2>&1)"
546
+ code7p=$?
547
+ if [[ $code7p -eq 0 ]]; then
548
+ _pass "fresh-matching-bundle: reconciler exits 0 (matching commit_sha -- not treated as stale)"
549
+ else
550
+ _fail "fresh-matching-bundle: expected exit 0, got $code7p -- output: $out7p"
551
+ fi
552
+ if echo "$out7p" | grep -qF "stale bundle ignored"; then
553
+ _fail "fresh-matching-bundle: must NOT emit the stale-bundle line for a matching commit_sha"
554
+ else
555
+ _pass "fresh-matching-bundle: does not emit the stale-bundle line"
556
+ fi
557
+ if echo "$out7p" | grep -qF "RECONCILED"; then
558
+ _pass "fresh-matching-bundle: Step 2 reconciled the claimed-pass command (RECONCILED shown) -- no behavior change for owned bundles"
559
+ else
560
+ _fail "fresh-matching-bundle: expected 'RECONCILED' in output -- output: $out7p"
561
+ fi
562
+
563
+ # 7q/7r. REAL-GIT-REPO coverage for the ancestor branch of bundleAttestsThisChange()
564
+ # (iteration-4 MEDIUM finding: the ancestor path had zero coverage -- 7i/7p above only
565
+ # exercise exact-sha equality, never a real `git merge-base --is-ancestor` resolution).
566
+ # Both cases below use an ACTUAL git repo (not synthetic sha strings) with a real
567
+ # parent -> child commit pair.
568
+
569
+ GIT_ORIGIN="$DECLARED_TMPROOT/git-origin"
570
+ mkdir -p "$GIT_ORIGIN"
571
+ git -C "$GIT_ORIGIN" init -q
572
+ git -C "$GIT_ORIGIN" config user.email "test@example.com"
573
+ git -C "$GIT_ORIGIN" config user.name "Test"
574
+ echo "parent" > "$GIT_ORIGIN/file.txt"
575
+ git -C "$GIT_ORIGIN" add file.txt
576
+ git -C "$GIT_ORIGIN" commit -q -m "parent commit"
577
+ PARENT_SHA="$(git -C "$GIT_ORIGIN" rev-parse HEAD)"
578
+ echo "child" >> "$GIT_ORIGIN/file.txt"
579
+ git -C "$GIT_ORIGIN" add file.txt
580
+ git -C "$GIT_ORIGIN" commit -q -m "child commit"
581
+ CHILD_SHA="$(git -C "$GIT_ORIGIN" rev-parse HEAD)"
582
+
583
+ # 7q. TRUE-POSITIVE ancestor coverage: checkpoint sealed at the PARENT commit (not equal
584
+ # to this change's own sha), full-history repo, change sha = the CHILD commit ->
585
+ # the ancestor branch of bundleAttestsThisChange() (real `git merge-base
586
+ # --is-ancestor`, not the exact-equality shortcut) resolves FRESH; Step 2 still
587
+ # reconciles the claimed-pass command normally.
588
+ CASE7Q="$GIT_ORIGIN"
589
+ FRESH_LABEL_Q="node -e 'process.exit(0)'"
590
+ write_fresh_bundle "$CASE7Q" "$FRESH_LABEL_Q"
591
+ write_stale_checkpoint "$CASE7Q" "$PARENT_SHA"
592
+ out7q="$(TRUST_RECONCILE_SHA="$CHILD_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_Q" \
593
+ node "$RECONCILE" --repo-root "$CASE7Q" 2>&1)"
594
+ code7q=$?
595
+ if [[ $code7q -eq 0 ]]; then
596
+ _pass "git-ancestor-fresh: reconciler exits 0 (parent-sha checkpoint resolves as an ancestor of the child change sha)"
597
+ else
598
+ _fail "git-ancestor-fresh: expected exit 0, got $code7q -- output: $out7q"
599
+ fi
600
+ if echo "$out7q" | grep -qF "stale bundle ignored"; then
601
+ _fail "git-ancestor-fresh: must NOT emit the stale-bundle line -- the parent IS a real ancestor of the child"
602
+ else
603
+ _pass "git-ancestor-fresh: does not emit the stale-bundle line (true ancestor resolution, not equality)"
604
+ fi
605
+ if echo "$out7q" | grep -qF "RECONCILED"; then
606
+ _pass "git-ancestor-fresh: Step 2 reconciled the claimed-pass command (RECONCILED shown)"
607
+ else
608
+ _fail "git-ancestor-fresh: expected 'RECONCILED' in output -- output: $out7q"
609
+ fi
610
+
611
+ # 7r. DEGRADED-BUT-SAFE shallow-clone coverage: a `git clone --depth 1` of the SAME repo
612
+ # (so the checked-out change sha is the same CHILD commit) lacks the PARENT commit
613
+ # object entirely -- `git merge-base --is-ancestor` cannot resolve it (exits 128) and
614
+ # isAncestorCommit() fails toward false, so this documents (not silently accepts) the
615
+ # current degraded mode: a real, legitimately-fresh bundle is treated as stale under a
616
+ # misconfigured shallow checkout, loudly, with the stale line naming both shas -- a
617
+ # future checkout-depth improvement would flip this assertion, not remove it.
618
+ CASE7R="$DECLARED_TMPROOT/git-shallow-clone"
619
+ git clone -q --depth 1 "file://$GIT_ORIGIN" "$CASE7R" 2>/dev/null
620
+ mkdir -p "$CASE7R/delivery"
621
+ cp "$GIT_ORIGIN/delivery/trust.bundle" "$CASE7R/delivery/trust.bundle"
622
+ write_stale_checkpoint "$CASE7R" "$PARENT_SHA"
623
+ out7r="$(TRUST_RECONCILE_SHA="$CHILD_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_Q" \
624
+ node "$RECONCILE" --repo-root "$CASE7R" 2>&1)"
625
+ code7r=$?
626
+ if [[ $code7r -ne 0 ]]; then
627
+ _pass "git-shallow-clone: reconciler exits non-zero ($code7r) -- shallow clone cannot resolve the ancestor check, fails toward stale"
628
+ else
629
+ _fail "git-shallow-clone: expected non-zero exit (degraded-but-safe), got 0 -- output: $out7r"
630
+ fi
631
+
632
+ # 7s/7t/7u. Event-scoped enforcement (ADR 0022 addendum, part 4) -- HIGH launch-blocker
633
+ # reproduction: after a squash-merge onto main, the squash commit has NO git ancestry to
634
+ # the feature-branch commit a checkpoint was sealed at (`git merge --squash` discards the
635
+ # original commit graph) -- on a push event this must be a loud no-op, not a failure; on
636
+ # pull_request the exact same shape must still fail closed (scoping did not weaken
637
+ # PR-time gating); and the absent-event default must still enforce.
638
+
639
+ SQUASH_REPO="$DECLARED_TMPROOT/git-squash-repo"
640
+ mkdir -p "$SQUASH_REPO"
641
+ git -C "$SQUASH_REPO" init -q
642
+ git -C "$SQUASH_REPO" config user.email "test@example.com"
643
+ git -C "$SQUASH_REPO" config user.name "Test"
644
+ echo "main" > "$SQUASH_REPO/file.txt"
645
+ git -C "$SQUASH_REPO" add file.txt
646
+ git -C "$SQUASH_REPO" commit -q -m "main tip"
647
+ git -C "$SQUASH_REPO" checkout -q -b feature
648
+ echo "feature work" >> "$SQUASH_REPO/file.txt"
649
+ git -C "$SQUASH_REPO" add file.txt
650
+ git -C "$SQUASH_REPO" commit -q -m "feature work"
651
+ FEATURE_TIP_SHA="$(git -C "$SQUASH_REPO" rev-parse HEAD)"
652
+ git -C "$SQUASH_REPO" checkout -q -
653
+ git -C "$SQUASH_REPO" merge -q --squash feature
654
+ git -C "$SQUASH_REPO" commit -q -m "squash-merge feature"
655
+ SQUASH_SHA="$(git -C "$SQUASH_REPO" rev-parse HEAD)"
656
+
657
+ # Sanity: confirm the fixture actually reproduces the reviewer's shape -- the feature tip
658
+ # must NOT be an ancestor of the squash commit (if it were, this fixture would not exercise
659
+ # the bug at all).
660
+ if git -C "$SQUASH_REPO" merge-base --is-ancestor "$FEATURE_TIP_SHA" "$SQUASH_SHA" 2>/dev/null; then
661
+ _fail "git-squash fixture sanity: feature tip unexpectedly IS an ancestor of the squash commit -- fixture does not reproduce the reviewer's shape"
662
+ else
663
+ _pass "git-squash fixture sanity: feature tip is NOT an ancestor of the squash commit (reproduces the reviewer's shape)"
664
+ fi
665
+
666
+ write_stale_checkpoint "$SQUASH_REPO" "$FEATURE_TIP_SHA"
667
+
668
+ # 7s. push event -> loud no-op, exit 0 (the reviewer's exact reproduction, now locked).
669
+ out7s="$(TRUST_RECONCILE_EVENT="push" TRUST_RECONCILE_SHA="$SQUASH_SHA" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
670
+ node "$RECONCILE" --repo-root "$SQUASH_REPO" 2>&1)"
671
+ code7s=$?
672
+ if [[ $code7s -eq 0 ]]; then
673
+ _pass "git-squash-push: reconciler exits 0 (post-merge push run is a no-op, not a failure)"
674
+ else
675
+ _fail "git-squash-push: expected exit 0, got $code7s -- output: $out7s"
676
+ fi
677
+ if echo "$out7s" | grep -qF "push event: inherited bundle does not attest this commit — skipping Step 2 (gating happened on the PR run)"; then
678
+ _pass "git-squash-push: emitted the exact push-event no-op line"
679
+ else
680
+ _fail "git-squash-push: expected the push-event no-op line -- output: $out7s"
681
+ fi
682
+ if echo "$out7s" | grep -qF "bundle-required-no-declared-marker"; then
683
+ _fail "git-squash-push: must NOT emit bundle-required-no-declared-marker on a push event"
684
+ else
685
+ _pass "git-squash-push: does not emit bundle-required-no-declared-marker (enforcement does not apply on push)"
686
+ fi
687
+
688
+ # 7t. SAME shape, event=pull_request -> still fails/stales as before (scoping did not
689
+ # weaken PR-time gating).
690
+ out7t="$(TRUST_RECONCILE_EVENT="pull_request" TRUST_RECONCILE_SHA="$SQUASH_SHA" TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
691
+ node "$RECONCILE" --repo-root "$SQUASH_REPO" 2>&1)"
692
+ code7t=$?
693
+ if [[ $code7t -ne 0 ]]; then
694
+ _pass "git-squash-pull-request: reconciler exits non-zero ($code7t) -- same squash shape still fails closed when gating a PR"
695
+ else
696
+ _fail "git-squash-pull-request: expected non-zero exit, got 0 -- output: $out7t"
697
+ fi
698
+ if echo "$out7t" | grep -qF "stale bundle ignored — attests $FEATURE_TIP_SHA, this change is $SQUASH_SHA"; then
699
+ _pass "git-squash-pull-request: emitted the exact stale-bundle line"
700
+ else
701
+ _fail "git-squash-pull-request: expected the stale-bundle line -- output: $out7t"
702
+ fi
703
+ if echo "$out7t" | grep -qF "bundle-required-no-declared-marker"; then
704
+ _pass "git-squash-pull-request: emitted bundle-required-no-declared-marker (PR-time gating unweakened)"
705
+ else
706
+ _fail "git-squash-pull-request: expected bundle-required-no-declared-marker -- output: $out7t"
707
+ fi
708
+
709
+ # 7u. Absent-event default explicitly asserted to enforce (7a already covers this
710
+ # implicitly for the no-marker case -- this makes the default assertion explicit).
711
+ CASE7U="$DECLARED_TMPROOT/absent-event-default-enforces"
712
+ mkdir -p "$CASE7U"
713
+ out7u="$(TRUST_RECONCILE_COMMANDS="$DECLARED_CMD" \
714
+ node "$RECONCILE" --repo-root "$CASE7U" 2>&1)"
715
+ code7u=$?
716
+ if [[ $code7u -ne 0 ]]; then
717
+ _pass "absent-event-default-enforces: reconciler exits non-zero ($code7u) -- no TRUST_RECONCILE_EVENT defaults to enforce"
718
+ else
719
+ _fail "absent-event-default-enforces: expected non-zero exit, got 0 -- output: $out7u"
720
+ fi
721
+ if echo "$out7u" | grep -qF "bundle-required-no-declared-marker"; then
722
+ _pass "absent-event-default-enforces: emitted bundle-required-no-declared-marker (conservative default confirmed)"
723
+ else
724
+ _fail "absent-event-default-enforces: expected bundle-required-no-declared-marker -- output: $out7u"
725
+ fi
726
+
727
+ # 7v. [MEDIUM, doc-residual follow-up] push event + a deliberately FAILING
728
+ # trust-reconcile-verify -> exit 1. Proves event-scoped bundle-required relaxation on
729
+ # push does NOT also relax Step 1 -- a red push run must never look green just because
730
+ # Step 2/delivery-DECLARED enforcement does not apply on push.
731
+ CASE7V="$DECLARED_TMPROOT/push-event-failing-verify"
732
+ mkdir -p "$CASE7V"
733
+ out7v="$(TRUST_RECONCILE_EVENT="push" TRUST_RECONCILE_COMMANDS="node -e 'process.exit(1)'" \
734
+ node "$RECONCILE" --repo-root "$CASE7V" 2>&1)"
735
+ code7v=$?
736
+ if [[ $code7v -ne 0 ]]; then
737
+ _pass "push-event-failing-verify: reconciler exits non-zero ($code7v) -- a failing Step 1 still fails on push, event scoping never masks a red run"
738
+ else
739
+ _fail "push-event-failing-verify: expected non-zero exit, got 0 -- output: $out7v"
740
+ fi
741
+ if echo "$out7v" | grep -qF "verification failed in CI"; then
742
+ _pass "push-event-failing-verify: emitted 'verification failed in CI' (Step 1 failure, unaffected by event scoping)"
743
+ else
744
+ _fail "push-event-failing-verify: expected 'verification failed in CI' -- output: $out7v"
745
+ fi
746
+ if echo "$out7v" | grep -qF "push event:"; then
747
+ _pass "push-event-failing-verify: still emits the push-event Step-2 no-op line even though the overall run fails on Step 1 (the two are independent)"
748
+ else
749
+ _fail "push-event-failing-verify: expected the push-event no-op line -- output: $out7v"
750
+ fi
751
+
144
752
  echo ""
145
753
  if [[ $errors -eq 0 ]]; then
146
754
  echo "test_trust_reconcile_negatives: all checks passed."