@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.
- package/.github/CODEOWNERS +8 -0
- package/.github/workflows/ci.yml +12 -0
- package/.github/workflows/trust-reconcile.yml +62 -4
- package/CHANGELOG.md +23 -0
- package/CONTEXT.md +21 -0
- package/build/src/cli/assignment-provider.d.ts +1 -0
- package/build/src/cli/assignment-provider.js +748 -0
- package/build/src/cli/effective-assignment-provider-settings.d.ts +1 -0
- package/build/src/cli/effective-assignment-provider-settings.js +125 -0
- package/build/src/cli/validate-workflow-artifacts.js +5 -1
- package/build/src/cli/workflow-sidecar.js +157 -110
- package/build/src/cli.js +6 -0
- package/build/src/tools/validate-source-tree.js +1 -0
- package/context/contracts/artifact-contract.md +2 -0
- package/context/contracts/assignment-provider-contract.md +239 -0
- package/context/contracts/builder-kit-workflow-state-contract.md +2 -0
- package/context/contracts/decision-registry-contract.md +2 -0
- package/context/contracts/delivery-contract.md +2 -0
- package/context/contracts/execution-contract.md +25 -0
- package/context/contracts/governance-adapter-contract.md +2 -0
- package/context/contracts/knowledge-store-contract.md +197 -0
- package/context/contracts/planning-contract.md +2 -0
- package/context/contracts/review-contract.md +2 -0
- package/context/contracts/sandbox-policy.md +2 -0
- package/context/contracts/standing-directives.md +13 -0
- package/context/contracts/verification-contract.md +2 -0
- package/context/contracts/work-item-contract.md +2 -0
- package/context/settings/assignment-provider-settings.json +33 -0
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +180 -0
- package/docs/context-map.md +1 -0
- package/docs/decisions/index.md +3 -0
- package/docs/decisions/knowledge-store-provider.md +51 -0
- package/docs/decisions/model-routing.md +63 -0
- package/docs/decisions/standing-directives.md +66 -0
- package/docs/fixture-ownership.md +1 -0
- package/docs/workflow-shared-contracts.md +2 -1
- package/docs/workflow-usage-guide.md +6 -0
- package/evals/ci/run-baseline.sh +6 -0
- package/evals/fixtures/assignment-provider/actor-a.json +6 -0
- package/evals/fixtures/assignment-provider/actor-b.json +6 -0
- package/evals/fixtures/assignment-provider/github-issue-claimed.json +27 -0
- package/evals/fixtures/assignment-provider/github-issue-unassigned.json +7 -0
- package/evals/fixtures/assignment-provider/liveness-fresh.json +9 -0
- package/evals/fixtures/assignment-provider/liveness-stale.json +9 -0
- package/evals/integration/test_assignment_provider_github.sh +318 -0
- package/evals/integration/test_assignment_provider_local_file.sh +222 -0
- package/evals/integration/test_critique_supersession_roundtrip.sh +182 -0
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_publish_delivery.sh +21 -4
- package/evals/integration/test_pull_work_assignment_join.sh +132 -0
- package/evals/integration/test_pull_work_liveness_preflight.sh +14 -6
- package/evals/integration/test_reconcile_soundness.sh +33 -9
- package/evals/integration/test_trust_reconcile.sh +9 -8
- package/evals/integration/test_trust_reconcile_negatives.sh +608 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +79 -0
- package/evals/run.sh +10 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +6 -0
- package/evals/static/test_knowledge_providers.sh +23 -0
- package/kits/builder/skills/deliver/SKILL.md +19 -0
- package/kits/builder/skills/pull-work/SKILL.md +78 -10
- package/kits/knowledge/kit.json +35 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/CONTEXT.md +12 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/old-sprocket-shape.md +13 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/sprocket-shape.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/decisions/widget-format.md +14 -0
- package/kits/knowledge/providers/conformance/fixtures/git-repo/docs/learnings/fixture-learning.md +7 -0
- package/kits/knowledge/providers/conformance/fixtures/work-item/issues.json +30 -0
- package/kits/knowledge/providers/conformance/suite.test.js +125 -0
- package/kits/knowledge/providers/git-repo/index.js +236 -0
- package/kits/knowledge/providers/health/health-pass.test.js +99 -0
- package/kits/knowledge/providers/health/index.js +153 -0
- package/kits/knowledge/providers/index.js +24 -0
- package/kits/knowledge/providers/lib/model.js +91 -0
- package/kits/knowledge/providers/lib/schema-validate.js +119 -0
- package/kits/knowledge/providers/markdown-vault/index.js +169 -0
- package/kits/knowledge/providers/work-item/index.js +204 -0
- package/package.json +4 -2
- package/schemas/assignment-provider-settings.schema.json +125 -0
- package/schemas/knowledge/edge.schema.json +54 -0
- package/schemas/knowledge/health-report.schema.json +45 -0
- package/schemas/knowledge/node.schema.json +49 -0
- package/schemas/knowledge/proposal.schema.json +53 -0
- package/scripts/ci/trust-reconcile.js +521 -24
- package/src/cli/assignment-provider.ts +845 -0
- package/src/cli/effective-assignment-provider-settings.ts +112 -0
- package/src/cli/validate-workflow-artifacts.ts +5 -1
- package/src/cli/workflow-sidecar.ts +147 -106
- package/src/cli.ts +6 -0
- 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."
|