@kontourai/flow-agents 2.2.0 → 2.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.
- package/.github/CODEOWNERS +8 -0
- package/.github/workflows/ci.yml +32 -0
- package/AGENTS.md +4 -4
- package/CHANGELOG.md +46 -0
- package/CONTEXT.md +14 -0
- package/README.md +9 -3
- package/agents/dev.json +1 -1
- package/agents/tool-code-reviewer.json +1 -1
- package/agents/tool-planner.json +3 -3
- package/agents/tool-verifier.json +3 -3
- package/build/src/cli/console-learning-projection.js +3 -2
- package/build/src/cli/init.js +104 -21
- package/build/src/cli/kit.js +37 -6
- package/build/src/cli/pull-work-provider.js +1 -1
- package/build/src/cli/usage-feedback.js +3 -3
- package/build/src/cli/validate-hook-influence.js +1 -0
- package/build/src/cli/validate-workflow-artifacts.js +22 -6
- package/build/src/cli/workflow-artifact-cleanup-audit.js +36 -2
- package/build/src/cli/workflow-sidecar.d.ts +14 -7
- package/build/src/cli/workflow-sidecar.js +792 -69
- package/build/src/flow-kit/validate.d.ts +23 -0
- package/build/src/flow-kit/validate.js +67 -1
- package/build/src/index.d.ts +1 -1
- package/build/src/index.js +1 -1
- package/build/src/lib/flow-resolver.d.ts +6 -4
- package/build/src/lib/flow-resolver.js +125 -58
- package/build/src/lib/local-artifact-root.d.ts +14 -2
- package/build/src/lib/local-artifact-root.js +22 -5
- package/build/src/lib/workflow-learning-projection.js +2 -2
- package/build/src/runtime-adapters.d.ts +12 -0
- package/build/src/runtime-adapters.js +56 -16
- package/build/src/tools/build-universal-bundles.js +49 -16
- package/build/src/tools/generate-context-map.js +10 -8
- package/build/src/tools/validate-source-tree.js +23 -1
- package/console.telemetry.json +22 -0
- package/context/contracts/artifact-contract.md +38 -7
- package/context/contracts/builder-kit-workflow-state-contract.md +3 -2
- package/context/contracts/decision-registry-contract.md +263 -0
- package/context/contracts/delivery-contract.md +1 -1
- package/context/contracts/planning-contract.md +2 -1
- package/context/contracts/review-contract.md +5 -3
- package/context/contracts/verification-contract.md +9 -1
- package/context/contracts/work-item-contract.md +1 -1
- package/context/deferred/parallelization.md +1 -1
- package/context/gate-awareness.md +4 -4
- package/context/scripts/git-status.sh +2 -2
- package/context/scripts/hooks/config-protection.js +502 -2
- package/context/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/context/scripts/hooks/run-hook.js +14 -1
- package/context/scripts/hooks/stop-goal-fit.js +1140 -99
- package/context/scripts/hooks/workflow-steering.js +212 -6
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +35 -0
- package/docs/adr/0019-kit-dependency-ownership.md +62 -0
- package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +290 -0
- package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +221 -0
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +283 -0
- package/docs/agent-system-guidebook.md +3 -3
- package/docs/agent-usage-feedback-loop.md +6 -6
- package/docs/context-map.md +9 -7
- package/docs/decisions/decision-records.md +57 -0
- package/docs/decisions/index.md +14 -0
- package/docs/decisions/promotion-gate.md +52 -0
- package/docs/developer-architecture.md +1 -1
- package/docs/fixture-ownership.md +3 -0
- package/docs/flow-kit-repository-contract.md +5 -5
- package/docs/getting-started.md +12 -2
- package/docs/integrations/harness-install.md +2 -1
- package/docs/kit-authoring-guide.md +64 -5
- package/docs/knowledge-kit.md +5 -1
- package/docs/learnings/2026-07-improvement-program.md +203 -0
- package/docs/migrations.md +6 -5
- package/docs/north-star.md +2 -2
- package/docs/operating-layers.md +4 -3
- package/docs/repository-structure.md +5 -5
- package/docs/skills-map.md +8 -8
- package/docs/spec/runtime-hook-surface.md +19 -15
- package/docs/spikes/graph-provider-2026-07.md +155 -0
- package/docs/standards-register.md +7 -7
- package/docs/trust-anchor-adoption.md +105 -4
- package/docs/work-item-adapters.md +1 -1
- package/docs/workflow-artifact-lifecycle.md +72 -9
- package/docs/workflow-eval-strategy.md +3 -3
- package/docs/workflow-shared-contracts.md +1 -1
- package/docs/workflow-usage-guide.md +254 -21
- package/evals/acceptance/DEMO-false-completion.md +20 -11
- package/evals/acceptance/prove-capture-teeth-declared.sh +15 -15
- package/evals/acceptance/prove-capture-teeth.sh +11 -11
- package/evals/acceptance/test_kiro_harness.sh +1 -1
- package/evals/ci/antigaming-suite.sh +5 -0
- package/evals/ci/run-baseline.sh +65 -1
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/invalid-bad-dependency/kit.json +13 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-with-dependency/kit.json +13 -0
- package/evals/fixtures/hook-influence/cases.json +32 -0
- package/evals/fixtures/pull-work-provider/github-issues.json +6 -6
- package/evals/fixtures/trust-reconcile-exploits/fabricated-attestation.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/no-label-bypass.json +36 -0
- package/evals/fixtures/trust-reconcile-exploits/skip-assumed-bypass.json +59 -0
- package/evals/fixtures/trust-reconcile-exploits/status-misassertion.json +48 -0
- package/evals/fixtures/trust-reconcile-exploits/waived-command-check.json +44 -0
- package/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json +159 -0
- package/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json +920 -0
- package/evals/integration/test_actor_identity.sh +254 -0
- package/evals/integration/test_builder_step_producers.sh +33 -23
- package/evals/integration/test_bundle_install.sh +22 -16
- package/evals/integration/test_bundle_lifecycle.sh +11 -11
- package/evals/integration/test_captured_fail_reconciliation.sh +50 -50
- package/evals/integration/test_checkpoint_signing.sh +43 -0
- package/evals/integration/test_claim_lookup.sh +4 -4
- package/evals/integration/test_codex_hook_resolution.sh +114 -0
- package/evals/integration/test_command_log_concurrency.sh +8 -8
- package/evals/integration/test_command_log_fork_classification.sh +3 -3
- package/evals/integration/test_command_log_integrity.sh +16 -16
- package/evals/integration/test_console_learning_projection.sh +1 -1
- package/evals/integration/test_dual_emit_flow_step.sh +145 -0
- package/evals/integration/test_enforcer_expects_driven.sh +12 -12
- package/evals/integration/test_evidence_capture_hook.sh +14 -13
- package/evals/integration/test_fixture_retirement_audit.sh +2 -2
- package/evals/integration/test_flow_agents_statusline.sh +2 -2
- package/evals/integration/test_flow_kit_install_git.sh +52 -0
- package/evals/integration/test_flow_kit_repository.sh +2 -0
- package/evals/integration/test_flowdef_session_activation.sh +6 -6
- package/evals/integration/test_flowdef_union_floor_regression.sh +429 -0
- package/evals/integration/test_gate_bypass_chain.sh +52 -40
- package/evals/integration/test_gate_lockdown.sh +97 -73
- package/evals/integration/test_gate_review_inquiry_records.sh +3 -3
- package/evals/integration/test_goal_fit_escape_hatch.sh +10 -10
- package/evals/integration/test_goal_fit_ghost_session.sh +104 -0
- package/evals/integration/test_goal_fit_hook.sh +32 -32
- package/evals/integration/test_goal_fit_rederive.sh +9 -9
- package/evals/integration/test_hook_category_behaviors.sh +103 -0
- package/evals/integration/test_hook_influence_cases.sh +1 -0
- package/evals/integration/test_install_merge.sh +262 -1
- package/evals/integration/test_kit_identity_trust.sh +6 -6
- package/evals/integration/test_liveness_conflict_injection.sh +587 -0
- package/evals/integration/test_liveness_heartbeat.sh +677 -0
- package/evals/integration/test_liveness_verdict.sh +394 -0
- package/evals/integration/test_local_flow_kit_install.sh +15 -0
- package/evals/integration/test_phase_map_and_gate_claim.sh +63 -11
- package/evals/integration/test_promote_gate.sh +178 -0
- package/evals/integration/test_publish_delivery.sh +2 -2
- package/evals/integration/test_pull_work_liveness_preflight.sh +386 -0
- package/evals/integration/test_pull_work_provider.sh +2 -2
- package/evals/integration/test_reconcile_soundness.sh +4 -4
- package/evals/integration/test_resolvefirststep_security.sh +22 -0
- package/evals/integration/test_runtime_adapter_activation.sh +87 -18
- package/evals/integration/test_session_resume_roundtrip.sh +147 -0
- package/evals/integration/test_sidecar_field_preservation.sh +249 -0
- package/evals/integration/test_trust_reconcile.sh +2 -2
- package/evals/integration/test_trust_reconcile_manifest.sh +79 -0
- package/evals/integration/test_trust_reconcile_mixed_bundle.sh +96 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +151 -0
- package/evals/integration/test_usage_feedback_global.sh +5 -5
- package/evals/integration/test_validate_artifacts_portability.sh +60 -0
- package/evals/integration/test_verify_cli.sh +2 -2
- package/evals/integration/test_veritas_governance_kit.sh +117 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +26 -1
- package/evals/integration/test_workflow_artifacts.sh +33 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +723 -74
- package/evals/integration/test_workflow_steering_hook.sh +175 -5
- package/evals/lib/codex-provider.sh +1 -1
- package/evals/lib/node.sh +19 -1
- package/evals/run.sh +22 -0
- package/evals/static/test_decisions.sh +150 -0
- package/evals/static/test_flowdef_codeowners_coverage.sh +56 -0
- package/evals/static/test_package.sh +19 -10
- package/evals/static/test_universal_bundles.sh +48 -3
- package/evals/static/test_workflow_skills.sh +41 -8
- package/kits/builder/flows/build.flow.json +3 -74
- package/kits/builder/flows/publish-learn.flow.json +90 -0
- package/kits/builder/kit.json +11 -0
- package/kits/builder/skills/builder-shape/SKILL.md +1 -1
- package/kits/builder/skills/deliver/SKILL.md +13 -11
- package/kits/builder/skills/design-probe/SKILL.md +4 -4
- package/kits/builder/skills/evidence-gate/SKILL.md +26 -1
- package/kits/builder/skills/execute-plan/SKILL.md +2 -2
- package/kits/builder/skills/fix-bug/SKILL.md +2 -0
- package/kits/builder/skills/gate-review/SKILL.md +6 -6
- package/kits/builder/skills/idea-to-backlog/SKILL.md +1 -1
- package/kits/builder/skills/learning-review/SKILL.md +7 -7
- package/kits/builder/skills/pickup-probe/SKILL.md +5 -4
- package/kits/builder/skills/plan-work/SKILL.md +27 -5
- package/kits/builder/skills/pull-work/SKILL.md +80 -5
- package/kits/builder/skills/release-readiness/SKILL.md +4 -4
- package/kits/builder/skills/review-work/SKILL.md +24 -1
- package/kits/builder/skills/tdd-workflow/SKILL.md +2 -0
- package/kits/builder/skills/verify-work/SKILL.md +8 -1
- package/kits/catalog.json +6 -0
- package/kits/veritas-governance/adapter/readiness-to-trust-bundle.mjs +178 -0
- package/kits/veritas-governance/docs/README.md +75 -0
- package/kits/veritas-governance/fixtures/readiness/not-ready.readiness-report.json +4645 -0
- package/kits/veritas-governance/fixtures/readiness/ready.readiness-report.json +4403 -0
- package/kits/veritas-governance/flows/readiness-check.flow.json +35 -0
- package/kits/veritas-governance/kit.json +16 -0
- package/package.json +8 -4
- package/packaging/README.md +2 -2
- package/packaging/conformance/fixtures/config-protection--allow-read-state-json.json +20 -0
- package/packaging/conformance/fixtures/config-protection--block-write-state-json.json +20 -0
- package/packaging/conformance/fixtures/stop-goal-fit--block-bundle-disputed-claim.json +1 -1
- package/packaging/conformance/fixtures/stop-goal-fit--block-capture-contradicts-claimed-pass.json +1 -1
- package/packaging/manifest.json +9 -10
- package/schemas/decision-record.schema.json +109 -0
- package/schemas/workflow-evidence.schema.json +1 -1
- package/schemas/workflow-state.schema.json +5 -0
- package/scripts/README.md +8 -3
- package/scripts/check-content-boundary.cjs +1 -1
- package/scripts/check-decisions.cjs +356 -0
- package/scripts/ci/derive-claim-status.mjs +74 -0
- package/scripts/ci/trust-reconcile.js +436 -79
- package/scripts/git-status.sh +2 -2
- package/scripts/hooks/claude-telemetry-hook.js +37 -3
- package/scripts/hooks/codex-telemetry-hook.js +36 -2
- package/scripts/hooks/config-protection.js +102 -30
- package/scripts/hooks/evidence-capture.js +3 -3
- package/scripts/hooks/lib/actor-identity.js +368 -0
- package/scripts/hooks/lib/config-protection-remedies.js +69 -0
- package/scripts/hooks/lib/liveness-heartbeat.js +361 -0
- package/scripts/hooks/lib/liveness-policy.js +127 -0
- package/scripts/hooks/lib/liveness-read.js +105 -12
- package/scripts/hooks/lib/liveness-write.js +46 -0
- package/scripts/hooks/lib/local-artifact-paths.js +12 -6
- package/scripts/hooks/opencode-telemetry-hook.js +27 -1
- package/scripts/hooks/pi-telemetry-hook.js +27 -1
- package/scripts/hooks/stop-goal-fit.js +157 -26
- package/scripts/hooks/workflow-steering.js +77 -3
- package/scripts/install-codex-home.sh +138 -38
- package/scripts/statusline/flow-agents-statusline.js +2 -2
- package/src/cli/console-learning-projection.ts +3 -2
- package/src/cli/init.ts +101 -21
- package/src/cli/kit.ts +37 -6
- package/src/cli/public-api.test.mjs +58 -6
- package/src/cli/pull-work-provider.ts +1 -1
- package/src/cli/trust-bundle-policy-order.test.mjs +87 -0
- package/src/cli/usage-feedback.ts +3 -3
- package/src/cli/validate-hook-influence.ts +1 -0
- package/src/cli/validate-workflow-artifacts.ts +22 -6
- package/src/cli/veritas-readiness-adapter.test.mjs +267 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +35 -2
- package/src/cli/workflow-sidecar.ts +795 -71
- package/src/flow-kit/validate.ts +74 -1
- package/src/index.ts +6 -2
- package/src/lib/flow-resolver.ts +123 -55
- package/src/lib/local-artifact-root.ts +24 -5
- package/src/lib/workflow-learning-projection.ts +2 -2
- package/src/runtime-adapters.ts +61 -15
- package/src/tools/build-universal-bundles.ts +49 -16
- package/src/tools/generate-context-map.ts +10 -8
- package/src/tools/validate-source-tree.ts +23 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_sidecar_field_preservation.sh — CLASS-level regression guard for the #270/#298/#309
|
|
3
|
+
# field-loss bug family (silent persistent-field drop on a state.json-writing sidecar command).
|
|
4
|
+
#
|
|
5
|
+
# #289 introduced `branch:` as a persistent identity field; #309 found init-plan's initSidecars
|
|
6
|
+
# rewrite could silently DROP it on a repaired/backfilled session (a live instance of the same
|
|
7
|
+
# "state.json fully rewritten, not merged" bug class that #270/#298 already hit for other fields).
|
|
8
|
+
# code-review-309 found the SAME class live again: the #309 backfill path silently reset
|
|
9
|
+
# created_at on a repaired session, because initSidecars always re-stamps created_at from the
|
|
10
|
+
# current call's timestamp instead of preserving an existing session's original creation time.
|
|
11
|
+
#
|
|
12
|
+
# Rather than adding another one-field regression test, this is an INVARIANT SWEEP: seed one
|
|
13
|
+
# fully-populated session, run every state.json-touching mutator subcommand once (in the order a
|
|
14
|
+
# real session actually calls them), and after EACH command assert that every persistent identity
|
|
15
|
+
# field (branch, task_slug, repo, created_at, and owner when present) is byte-identical to the
|
|
16
|
+
# seeded baseline — except the fields a given command explicitly owns (status, phase, updated_at,
|
|
17
|
+
# next_action, artifact_paths). Any FUTURE writer that starts fully rewriting state.json instead
|
|
18
|
+
# of merging prior contents (the root cause of this whole bug family) fails this sweep immediately,
|
|
19
|
+
# naming the offending command — without anyone having to think to add a bespoke field-loss test
|
|
20
|
+
# for it first.
|
|
21
|
+
#
|
|
22
|
+
# Deterministic, no model spend, self-cleaning.
|
|
23
|
+
# Usage: bash evals/integration/test_sidecar_field_preservation.sh
|
|
24
|
+
|
|
25
|
+
set -uo pipefail
|
|
26
|
+
|
|
27
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
28
|
+
source "$ROOT/evals/lib/node.sh"
|
|
29
|
+
|
|
30
|
+
WRITER="workflow-sidecar"
|
|
31
|
+
TMPDIR_EVAL="$(mktemp -d)"
|
|
32
|
+
errors=0
|
|
33
|
+
|
|
34
|
+
cleanup() { rm -rf "$TMPDIR_EVAL"; }
|
|
35
|
+
trap cleanup EXIT
|
|
36
|
+
|
|
37
|
+
_pass() { echo " ✓ $1"; }
|
|
38
|
+
_fail() { echo " ✗ $1"; errors=$((errors + 1)); }
|
|
39
|
+
|
|
40
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
41
|
+
echo "jq is required for this eval's field-identity assertions" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
SESSION_ROOT="$TMPDIR_EVAL/repo/.kontourai/flow-agents"
|
|
46
|
+
SLUG="sidecar-field-preservation-sweep"
|
|
47
|
+
SESSION_DIR="$SESSION_ROOT/$SLUG"
|
|
48
|
+
STATE="$SESSION_DIR/state.json"
|
|
49
|
+
mkdir -p "$SESSION_ROOT"
|
|
50
|
+
|
|
51
|
+
# ─── Field-identity assertion helpers ────────────────────────────────────────
|
|
52
|
+
# Persistent identity fields captured from the seeded baseline. `owner` is included per the
|
|
53
|
+
# schema (schemas/workflow-state.schema.json allows it) even though NO current sidecar writer
|
|
54
|
+
# path ever stamps it — no command sets `owner:` on state.json today, so the captured baseline
|
|
55
|
+
# value is always the jq sentinel "<absent>" and stays "<absent>" for the whole sweep. That is
|
|
56
|
+
# still a real (trivial) assertion: if some future writer starts adding an `owner` field without
|
|
57
|
+
# preserving a pre-existing one, this sweep would catch it the same way it catches every other
|
|
58
|
+
# persistent field. No fixture forces a synthetic owner value — that would test a code path that
|
|
59
|
+
# does not exist today.
|
|
60
|
+
FIELDS=(branch task_slug repo created_at owner)
|
|
61
|
+
declare -A BASELINE
|
|
62
|
+
|
|
63
|
+
_field() {
|
|
64
|
+
local file="$1" field="$2"
|
|
65
|
+
jq -r --arg f "$field" '.[$f] // "<absent>"' "$file" 2>/dev/null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_capture_baseline() {
|
|
69
|
+
local field
|
|
70
|
+
for field in "${FIELDS[@]}"; do
|
|
71
|
+
BASELINE["$field"]="$(_field "$STATE" "$field")"
|
|
72
|
+
done
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_assert_preserved() {
|
|
76
|
+
local label="$1"
|
|
77
|
+
local field actual ok=1
|
|
78
|
+
for field in "${FIELDS[@]}"; do
|
|
79
|
+
actual="$(_field "$STATE" "$field")"
|
|
80
|
+
if [[ "$actual" != "${BASELINE[$field]}" ]]; then
|
|
81
|
+
_fail "$label dropped/changed persistent field '$field': expected '${BASELINE[$field]}' got '$actual'"
|
|
82
|
+
ok=0
|
|
83
|
+
fi
|
|
84
|
+
done
|
|
85
|
+
[[ "$ok" -eq 1 ]] && _pass "persistent identity fields (${FIELDS[*]}) survive $label"
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# ─── Seed: ensure-session with an injected actor (derived branch) and a KNOWN timestamp ──────
|
|
89
|
+
SEED_TS="2026-06-20T08:00:00Z"
|
|
90
|
+
if ! flow_agents_node "$WRITER" ensure-session \
|
|
91
|
+
--artifact-root "$SESSION_ROOT" \
|
|
92
|
+
--task-slug "$SLUG" \
|
|
93
|
+
--actor sweep-actor \
|
|
94
|
+
--flow-id builder.build \
|
|
95
|
+
--title "Sidecar Field Preservation Sweep" \
|
|
96
|
+
--source-request "Seed a fully-populated session for the field-preservation invariant sweep." \
|
|
97
|
+
--summary "Seed session for the class-level field-preservation sweep." \
|
|
98
|
+
--criterion "Every state.json-touching mutator preserves persistent identity fields" \
|
|
99
|
+
--timestamp "$SEED_TS" >"$TMPDIR_EVAL/seed.out" 2>"$TMPDIR_EVAL/seed.err"; then
|
|
100
|
+
_fail "sweep setup: ensure-session failed: $(cat "$TMPDIR_EVAL/seed.out" "$TMPDIR_EVAL/seed.err")"
|
|
101
|
+
echo ""
|
|
102
|
+
echo "$errors check(s) failed."
|
|
103
|
+
exit 1
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if [[ ! -f "$STATE" ]]; then
|
|
107
|
+
_fail "sweep setup: ensure-session did not write state.json"
|
|
108
|
+
echo ""
|
|
109
|
+
echo "$errors check(s) failed."
|
|
110
|
+
exit 1
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
_capture_baseline
|
|
114
|
+
if [[ "${BASELINE[branch]}" != "agent/sweep-actor/$SLUG" || "${BASELINE[created_at]}" != "$SEED_TS" || "${BASELINE[task_slug]}" != "$SLUG" ]]; then
|
|
115
|
+
_fail "sweep setup: seeded baseline did not carry the expected branch/created_at/task_slug (branch=${BASELINE[branch]} created_at=${BASELINE[created_at]} task_slug=${BASELINE[task_slug]})"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# ─── 1. init-plan, run against a BRANCH-LESS plan artifact ───────────────────────────────────
|
|
119
|
+
# Mirrors the real tool-planner shape: init-plan is invoked against the plan artifact
|
|
120
|
+
# ("<slug>--plan-work.md"), a DIFFERENT file than "<slug>--deliver.md" that ensure-session seeded
|
|
121
|
+
# the branch: line into. This is the exact repro shape of the #309 regression.
|
|
122
|
+
PLAN_ARTIFACT="$SESSION_DIR/$SLUG--plan-work.md"
|
|
123
|
+
cat > "$PLAN_ARTIFACT" <<'MARKDOWN'
|
|
124
|
+
---
|
|
125
|
+
role: plan
|
|
126
|
+
parent: sidecar-field-preservation-sweep--deliver
|
|
127
|
+
created: 2026-06-20
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
# Plan: field-preservation sweep fixture
|
|
131
|
+
|
|
132
|
+
A plan artifact deliberately carrying no `branch:` line (mirrors real tool-planner output).
|
|
133
|
+
|
|
134
|
+
## Definition Of Done
|
|
135
|
+
|
|
136
|
+
- **Acceptance criteria:**
|
|
137
|
+
- [ ] init-plan preserves persistent identity fields - Evidence: pending.
|
|
138
|
+
MARKDOWN
|
|
139
|
+
if flow_agents_node "$WRITER" init-plan "$PLAN_ARTIFACT" \
|
|
140
|
+
--source-request "Plan artifact carries no branch: line (mirrors tool-planner output)." \
|
|
141
|
+
--summary "Planning sidecars initialized from a branch-less plan artifact." \
|
|
142
|
+
--next-action "Advance to execution." \
|
|
143
|
+
--timestamp "2026-06-20T08:05:00Z" >"$TMPDIR_EVAL/init-plan.out" 2>"$TMPDIR_EVAL/init-plan.err"; then
|
|
144
|
+
_assert_preserved "init-plan"
|
|
145
|
+
else
|
|
146
|
+
_fail "init-plan failed: $(cat "$TMPDIR_EVAL/init-plan.out" "$TMPDIR_EVAL/init-plan.err")"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# ─── 2. advance-state (--flow-definition builder.build, non-terminal) ────────────────────────
|
|
150
|
+
# Moves phase execution -> maps to the "execute" step in builder.build's phase_map, which also
|
|
151
|
+
# sets current.json active_step_id so record-gate-claim below has a reachable gate.
|
|
152
|
+
if flow_agents_node "$WRITER" advance-state "$SESSION_DIR" \
|
|
153
|
+
--status in_progress \
|
|
154
|
+
--phase execution \
|
|
155
|
+
--summary "Execution started." \
|
|
156
|
+
--next-action "Implement the change." \
|
|
157
|
+
--flow-definition builder.build \
|
|
158
|
+
--timestamp "2026-06-20T08:10:00Z" >"$TMPDIR_EVAL/advance-1.out" 2>"$TMPDIR_EVAL/advance-1.err"; then
|
|
159
|
+
_assert_preserved "advance-state (#1, builder.build execution)"
|
|
160
|
+
else
|
|
161
|
+
_fail "advance-state (#1) failed: $(cat "$TMPDIR_EVAL/advance-1.out" "$TMPDIR_EVAL/advance-1.err")"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# ─── 3. record-agent-event ────────────────────────────────────────────────────────────────────
|
|
165
|
+
# record-agent-event does not write state.json at all today (only agents/<id>/events.jsonl and
|
|
166
|
+
# current.json active_agents) — this still pins that invariant: state.json must stay untouched.
|
|
167
|
+
if flow_agents_node "$WRITER" record-agent-event \
|
|
168
|
+
--artifact-root "$SESSION_ROOT" \
|
|
169
|
+
--agent-id tool-worker-sweep \
|
|
170
|
+
--kind evidence \
|
|
171
|
+
--status active \
|
|
172
|
+
--summary "Worker started the sweep's implementation pass." \
|
|
173
|
+
--timestamp "2026-06-20T08:15:00Z" >"$TMPDIR_EVAL/agent-event.out" 2>"$TMPDIR_EVAL/agent-event.err"; then
|
|
174
|
+
_assert_preserved "record-agent-event"
|
|
175
|
+
else
|
|
176
|
+
_fail "record-agent-event failed: $(cat "$TMPDIR_EVAL/agent-event.out" "$TMPDIR_EVAL/agent-event.err")"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# ─── 4. record-evidence (one check) ───────────────────────────────────────────────────────────
|
|
180
|
+
if flow_agents_node "$WRITER" record-evidence "$SESSION_DIR" \
|
|
181
|
+
--verdict pass \
|
|
182
|
+
--check-json '{"id":"sweep-check","kind":"command","status":"pass","summary":"Sweep fixture command check."}' \
|
|
183
|
+
--timestamp "2026-06-20T08:20:00Z" >"$TMPDIR_EVAL/evidence.out" 2>"$TMPDIR_EVAL/evidence.err"; then
|
|
184
|
+
_assert_preserved "record-evidence"
|
|
185
|
+
else
|
|
186
|
+
_fail "record-evidence failed: $(cat "$TMPDIR_EVAL/evidence.out" "$TMPDIR_EVAL/evidence.err")"
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
# ─── 5. record-critique (one finding) ─────────────────────────────────────────────────────────
|
|
190
|
+
if flow_agents_node "$WRITER" record-critique "$SESSION_DIR" \
|
|
191
|
+
--id sweep-review \
|
|
192
|
+
--reviewer tool-code-reviewer \
|
|
193
|
+
--verdict pass \
|
|
194
|
+
--summary "No blocking findings." \
|
|
195
|
+
--finding-json '{"id":"sweep-finding","severity":"low","status":"fixed","description":"No blocking issues found during sweep sanity critique."}' \
|
|
196
|
+
--timestamp "2026-06-20T08:25:00Z" >"$TMPDIR_EVAL/critique.out" 2>"$TMPDIR_EVAL/critique.err"; then
|
|
197
|
+
_assert_preserved "record-critique"
|
|
198
|
+
else
|
|
199
|
+
_fail "record-critique failed: $(cat "$TMPDIR_EVAL/critique.out" "$TMPDIR_EVAL/critique.err")"
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
# ─── 6. record-gate-claim (a builder.build-reachable expectation) ────────────────────────────
|
|
203
|
+
# advance-state #1 above (--phase execution) set current.json active_step_id to "execute" via
|
|
204
|
+
# builder.build's phase_map. The execute-gate in kits/builder/flows/build.flow.json declares
|
|
205
|
+
# exactly ONE expects[] entry ("implementation-scope"), so it auto-resolves without needing
|
|
206
|
+
# --expectation. This IS reachable in this scratch fixture (unlike, say, the verify-gate's two
|
|
207
|
+
# expects entries, which would need an explicit --expectation and a route-back dance to reach
|
|
208
|
+
# cleanly) — so no command in this sweep needed to be skipped.
|
|
209
|
+
if flow_agents_node "$WRITER" record-gate-claim "$SESSION_DIR" \
|
|
210
|
+
--status pass \
|
|
211
|
+
--summary "Implementation scope recorded for the sweep fixture." \
|
|
212
|
+
--timestamp "2026-06-20T08:30:00Z" >"$TMPDIR_EVAL/gate-claim.out" 2>"$TMPDIR_EVAL/gate-claim.err"; then
|
|
213
|
+
_assert_preserved "record-gate-claim"
|
|
214
|
+
else
|
|
215
|
+
_fail "record-gate-claim failed: $(cat "$TMPDIR_EVAL/gate-claim.out" "$TMPDIR_EVAL/gate-claim.err")"
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
# ─── 7. record-learning (correction.needed:false shape) ──────────────────────────────────────
|
|
219
|
+
if flow_agents_node "$WRITER" record-learning "$SESSION_DIR" \
|
|
220
|
+
--status learned \
|
|
221
|
+
--record-json '{"id":"sweep-learning","source_refs":["state.json","trust.bundle"],"outcome":"success","facts":["Field-preservation sweep completed without a persistent-field regression."],"interpretation":"Every state.json-touching mutator preserved the seeded identity fields.","routing":[{"target":"none","action":"No follow-up required.","status":"completed"}],"correction":{"needed":false,"evidence":"All persistent identity fields matched the seeded baseline after every mutator."}}' \
|
|
222
|
+
--summary "Learning recorded; no follow-up remains." \
|
|
223
|
+
--timestamp "2026-06-20T08:35:00Z" >"$TMPDIR_EVAL/learning.out" 2>"$TMPDIR_EVAL/learning.err"; then
|
|
224
|
+
_assert_preserved "record-learning"
|
|
225
|
+
else
|
|
226
|
+
_fail "record-learning failed: $(cat "$TMPDIR_EVAL/learning.out" "$TMPDIR_EVAL/learning.err")"
|
|
227
|
+
fi
|
|
228
|
+
|
|
229
|
+
# ─── 8. advance-state (second call) ──────────────────────────────────────────────────────────
|
|
230
|
+
# record-learning above already moved phase to "learning" (writeState), so an accepted/archived
|
|
231
|
+
# target here is allowed by advance-state's terminal-jump guard (prev.phase === "learning").
|
|
232
|
+
if flow_agents_node "$WRITER" advance-state "$SESSION_DIR" \
|
|
233
|
+
--status archived \
|
|
234
|
+
--phase learning \
|
|
235
|
+
--summary "Sweep session closed out." \
|
|
236
|
+
--next-action "None." \
|
|
237
|
+
--timestamp "2026-06-20T08:40:00Z" >"$TMPDIR_EVAL/advance-2.out" 2>"$TMPDIR_EVAL/advance-2.err"; then
|
|
238
|
+
_assert_preserved "advance-state (#2, close-out)"
|
|
239
|
+
else
|
|
240
|
+
_fail "advance-state (#2) failed: $(cat "$TMPDIR_EVAL/advance-2.out" "$TMPDIR_EVAL/advance-2.err")"
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
echo ""
|
|
244
|
+
if [[ "$errors" -eq 0 ]]; then
|
|
245
|
+
echo "test_sidecar_field_preservation: all checks passed."
|
|
246
|
+
else
|
|
247
|
+
echo "test_sidecar_field_preservation: $errors check(s) failed."
|
|
248
|
+
fi
|
|
249
|
+
exit "$errors"
|
|
@@ -46,7 +46,7 @@ const fs = require('fs');
|
|
|
46
46
|
const [,, bundlePath, label, passingStr] = process.argv;
|
|
47
47
|
const passing = passingStr === 'true';
|
|
48
48
|
const bundle = {
|
|
49
|
-
schemaVersion:
|
|
49
|
+
schemaVersion: 5,
|
|
50
50
|
source: "test-fixture",
|
|
51
51
|
claims: [
|
|
52
52
|
{
|
|
@@ -55,7 +55,7 @@ const bundle = {
|
|
|
55
55
|
value: passing ? "pass" : "fail",
|
|
56
56
|
status: passing ? "verified" : "disputed",
|
|
57
57
|
subjectId: "test-slug/build",
|
|
58
|
-
|
|
58
|
+
facet: "flow-agents.workflow",
|
|
59
59
|
subjectType: "workflow-check",
|
|
60
60
|
fieldOrBehavior: "build",
|
|
61
61
|
createdAt: "2026-06-27T00:00:00Z",
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_trust_reconcile_manifest.sh — WS8 (ADR 0020) manifest anti-gaming self-check.
|
|
3
|
+
#
|
|
4
|
+
# The reconcile manifest is evals/ci/run-baseline.sh's LANE_* registry (emitted via
|
|
5
|
+
# `--manifest-json`). This test proves the anti-gaming property design question 1 asked
|
|
6
|
+
# for — "a command in the manifest must actually run in a required lane" — holds by
|
|
7
|
+
# construction, and STAYS holding: it asserts the manifest id set and the set of checks
|
|
8
|
+
# invoked by .github/workflows/ci.yml's REQUIRED jobs are IDENTICAL. The advisory
|
|
9
|
+
# usage-feedback lane (continue-on-error, non-blocking) is excluded from BOTH the manifest and
|
|
10
|
+
# this required-set comparison (ADR 0020 iteration 2).
|
|
11
|
+
#
|
|
12
|
+
# 1. Every ci.yml `run-baseline.sh --check <slug>` invocation is a manifest entry.
|
|
13
|
+
# → fails when a CHECKS entry is removed from EVERY LANE_* array (it drops out of
|
|
14
|
+
# --manifest-json but ci.yml still invokes it): the anti-gaming coverage regresses.
|
|
15
|
+
# 2. Every manifest entry is invoked by a required ci.yml job.
|
|
16
|
+
# → fails when a manifest/lane command is not wired into any required lane.
|
|
17
|
+
#
|
|
18
|
+
# Mirrors evals/ci/antigaming-suite.sh's own rationale (a protection with no required-lane
|
|
19
|
+
# coverage is not a real protection) applied reflexively to the manifest itself.
|
|
20
|
+
#
|
|
21
|
+
# Deterministic, no model spend, self-cleaning.
|
|
22
|
+
# Usage: bash evals/integration/test_trust_reconcile_manifest.sh
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
27
|
+
RUN_BASELINE="$ROOT/evals/ci/run-baseline.sh"
|
|
28
|
+
CI_YML="$ROOT/.github/workflows/ci.yml"
|
|
29
|
+
|
|
30
|
+
errors=0
|
|
31
|
+
_pass() { echo " PASS: $1"; }
|
|
32
|
+
_fail() { echo " FAIL: $1"; errors=$((errors + 1)); }
|
|
33
|
+
|
|
34
|
+
echo "=== WS8 manifest self-check: manifest (run-baseline.sh LANE_*) == ci.yml required --check set ==="
|
|
35
|
+
|
|
36
|
+
if [[ ! -f "$RUN_BASELINE" ]]; then _fail "run-baseline.sh not found at $RUN_BASELINE"; fi
|
|
37
|
+
if [[ ! -f "$CI_YML" ]]; then _fail "ci.yml not found at $CI_YML"; fi
|
|
38
|
+
|
|
39
|
+
# Manifest ids (only lane-covered checks are emitted).
|
|
40
|
+
MANIFEST_JSON="$(bash "$RUN_BASELINE" --manifest-json 2>/dev/null)"
|
|
41
|
+
MANIFEST_IDS="$(printf '%s' "$MANIFEST_JSON" | node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{const a=JSON.parse(d);for(const e of a){if(!e.lanes||!e.lanes.length){process.stderr.write("manifest entry with no lanes: "+e.id+"\n");process.exit(3);}console.log(e.id);}})' | sort -u)"
|
|
42
|
+
if [[ -z "$MANIFEST_IDS" ]]; then _fail "manifest is empty"; fi
|
|
43
|
+
|
|
44
|
+
# ci.yml required --check slugs. WS8 (ADR 0020, iteration 2): the usage-feedback lane is
|
|
45
|
+
# advisory (continue-on-error, non-blocking) and is intentionally EXCLUDED from the reconcile
|
|
46
|
+
# manifest, so its --check invocations are filtered out here too — the manifest is compared
|
|
47
|
+
# against the REQUIRED-lane --check set only. (All five usage-feedback checks slugify to the
|
|
48
|
+
# `usage-feedback-*` prefix and no required-lane check does, so the filter is exact.)
|
|
49
|
+
CI_SLUGS="$(grep -oE 'run-baseline\.sh --check [a-z0-9-]+' "$CI_YML" | awk '{print $NF}' | grep -v '^usage-feedback-' | sort -u)"
|
|
50
|
+
if [[ -z "$CI_SLUGS" ]]; then _fail "no required-lane run-baseline.sh --check invocations found in ci.yml"; fi
|
|
51
|
+
|
|
52
|
+
manifest_count="$(printf '%s\n' "$MANIFEST_IDS" | grep -c . )"
|
|
53
|
+
ci_count="$(printf '%s\n' "$CI_SLUGS" | grep -c . )"
|
|
54
|
+
echo " manifest entries: $manifest_count ; ci.yml --check invocations: $ci_count"
|
|
55
|
+
|
|
56
|
+
# Direction 1: every ci.yml --check slug must be a manifest entry.
|
|
57
|
+
missing_in_manifest="$(comm -23 <(printf '%s\n' "$CI_SLUGS") <(printf '%s\n' "$MANIFEST_IDS"))"
|
|
58
|
+
if [[ -z "$missing_in_manifest" ]]; then
|
|
59
|
+
_pass "every ci.yml required --check invocation is a manifest (lane-covered) entry"
|
|
60
|
+
else
|
|
61
|
+
_fail "ci.yml invokes checks that are NOT in the manifest (removed from every LANE_* array?): $(echo "$missing_in_manifest" | tr '\n' ' ')"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Direction 2: every manifest entry must be invoked by a required ci.yml job.
|
|
65
|
+
missing_in_ci="$(comm -23 <(printf '%s\n' "$MANIFEST_IDS") <(printf '%s\n' "$CI_SLUGS"))"
|
|
66
|
+
if [[ -z "$missing_in_ci" ]]; then
|
|
67
|
+
_pass "every manifest entry is invoked by a required ci.yml job (required-lane coverage by construction)"
|
|
68
|
+
else
|
|
69
|
+
_fail "manifest entries NOT wired into any required ci.yml job: $(echo "$missing_in_ci" | tr '\n' ' ')"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo ""
|
|
73
|
+
if [[ $errors -eq 0 ]]; then
|
|
74
|
+
echo "test_trust_reconcile_manifest: all checks passed."
|
|
75
|
+
exit 0
|
|
76
|
+
else
|
|
77
|
+
echo "test_trust_reconcile_manifest: $errors check(s) failed."
|
|
78
|
+
exit 1
|
|
79
|
+
fi
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_trust_reconcile_mixed_bundle.sh — WS8 (ADR 0020) END-TO-END PROOF.
|
|
3
|
+
#
|
|
4
|
+
# The literal capability PR #264 could not demonstrate: a single trust.bundle carrying
|
|
5
|
+
# honest, granular, MIXED evidence passes the REAL Trust Reconcile entrypoint:
|
|
6
|
+
# - one CI-reconcilable claim (evidenceType test_output; execution.label is a real
|
|
7
|
+
# reconcile-manifest command — run-baseline.sh's "Content boundary" check),
|
|
8
|
+
# - one honest session-local claim (human_attestation, Surface-verified, never claimed
|
|
9
|
+
# to be CI-re-runnable, no waiver needed),
|
|
10
|
+
# - one explicitly-waived accepted_gap claim (metadata.waiver, printed loudly).
|
|
11
|
+
#
|
|
12
|
+
# Runs the REAL scripts/ci/trust-reconcile.js (not a mock), resolving the manifest from
|
|
13
|
+
# THIS repo's live run-baseline.sh registry (--repo-root at the repo root). The manifest
|
|
14
|
+
# command is re-run FRESH by the reconciler; the session-local claim is accepted on its
|
|
15
|
+
# Surface status; the waiver is printed on a distinct WAIVED line. Exit 0.
|
|
16
|
+
#
|
|
17
|
+
# The fresh-verify (Step 1) command is a trivial pass so the proof is fast and focused on
|
|
18
|
+
# the reconcile/classification/waiver behavior (same pattern as the other reconcile evals);
|
|
19
|
+
# the manifest-matched command is genuinely re-run against the real checkout.
|
|
20
|
+
#
|
|
21
|
+
# Deterministic, no model spend, self-cleaning.
|
|
22
|
+
# Usage: bash evals/integration/test_trust_reconcile_mixed_bundle.sh
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
27
|
+
RECONCILE="$ROOT/scripts/ci/trust-reconcile.js"
|
|
28
|
+
FIXTURE="$ROOT/evals/fixtures/trust-reconcile-mixed-bundle/mixed-bundle.json"
|
|
29
|
+
|
|
30
|
+
errors=0
|
|
31
|
+
_pass() { echo " PASS: $1"; }
|
|
32
|
+
_fail() { echo " FAIL: $1"; errors=$((errors + 1)); }
|
|
33
|
+
|
|
34
|
+
echo "=== WS8 end-to-end proof: mixed-evidence bundle passes the real Trust Reconcile ==="
|
|
35
|
+
|
|
36
|
+
if [[ ! -f "$FIXTURE" ]]; then _fail "fixture not found at $FIXTURE"; fi
|
|
37
|
+
|
|
38
|
+
# Real reconciler entrypoint. Fresh-verify is a trivial pass; the manifest is resolved
|
|
39
|
+
# from this repo's live run-baseline.sh registry (via --repo-root "$ROOT").
|
|
40
|
+
out="$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
41
|
+
node "$RECONCILE" --bundle "$FIXTURE" --repo-root "$ROOT" 2>&1)"
|
|
42
|
+
exit_code=$?
|
|
43
|
+
|
|
44
|
+
echo "----- reconciler output -----"
|
|
45
|
+
echo "$out"
|
|
46
|
+
echo "-----------------------------"
|
|
47
|
+
|
|
48
|
+
if [[ $exit_code -eq 0 ]]; then
|
|
49
|
+
_pass "mixed-evidence bundle passes the real Trust Reconcile (exit 0) — the capability PR #264 lacked"
|
|
50
|
+
else
|
|
51
|
+
_fail "expected exit 0, got $exit_code"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if echo "$out" | grep -q "RECONCILED: 'npm run check:content-boundary --'"; then
|
|
55
|
+
_pass "the test_output claim reconciled against its manifest command (fresh CI re-run)"
|
|
56
|
+
else
|
|
57
|
+
_fail "expected a RECONCILED line for the manifest-matched command"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if echo "$out" | grep -q "ATTESTED (not independently verifiable at L0): 'c-session-local' (workflow.check.external) evidenceType=human_attestation"; then
|
|
61
|
+
_pass "the human_attestation claim was accepted as session-local, loudly marked ATTESTED (not flagged not-run)"
|
|
62
|
+
else
|
|
63
|
+
_fail "expected an ATTESTED (not independently verifiable at L0) line for the human_attestation claim"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# WS8 iteration-4 (converged finding): the honest mixed bundle's ATTESTED claim must also be
|
|
67
|
+
# counted in the loud summary line, not just printed inline — proves the count is real, not
|
|
68
|
+
# just cosmetic per-claim decoration.
|
|
69
|
+
if echo "$out" | grep -q "1 attested claim(s) accepted without independent verification"; then
|
|
70
|
+
_pass "the attested-claim summary count line is present and correct (1)"
|
|
71
|
+
else
|
|
72
|
+
_fail "expected the '1 attested claim(s) accepted without independent verification' summary line"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if echo "$out" | grep -q "WAIVED:"; then
|
|
76
|
+
_pass "the accepted_gap claim was printed on a distinct, loud WAIVED line"
|
|
77
|
+
else
|
|
78
|
+
_fail "expected a WAIVED line for the waived accepted_gap claim"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# Guard: the manifest must have resolved from the live run-baseline.sh registry, not the
|
|
82
|
+
# legacy single-command fallback (proves the resolver is wired to the real registry).
|
|
83
|
+
if echo "$out" | grep -qE "manifest: [0-9]+ entries \(source: (package.json:trust-reconcile-manifest|evals/ci/run-baseline.sh)"; then
|
|
84
|
+
_pass "manifest resolved from the live run-baseline.sh registry (not a synthetic one)"
|
|
85
|
+
else
|
|
86
|
+
_fail "manifest did not resolve from the live registry: $(echo "$out" | grep -i manifest | head -1)"
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
echo ""
|
|
90
|
+
if [[ $errors -eq 0 ]]; then
|
|
91
|
+
echo "test_trust_reconcile_mixed_bundle: all checks passed."
|
|
92
|
+
exit 0
|
|
93
|
+
else
|
|
94
|
+
echo "test_trust_reconcile_mixed_bundle: $errors check(s) failed."
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_trust_reconcile_negatives.sh — WS8 iteration-2 anti-gaming negative regressions.
|
|
3
|
+
#
|
|
4
|
+
# Every live exploit the reviewer/verifier reproduced against the WS8 reconciler is frozen
|
|
5
|
+
# here as a permanent negative fixture. Each asserts the REAL scripts/ci/trust-reconcile.js
|
|
6
|
+
# exits NON-ZERO and emits the SPECIFIC divergence string the fix introduced. If any fix is
|
|
7
|
+
# ever regressed, the corresponding exploit passes again and this eval (which runs in the
|
|
8
|
+
# required runtime-and-kit lane via antigaming-suite.sh) goes red.
|
|
9
|
+
#
|
|
10
|
+
# 1. no-label-bypass (finding 1) → not-run: a test_output claim with no
|
|
11
|
+
# manifest-matched execution.label is a divergence, never session-local.
|
|
12
|
+
# 2. skip-assumed-bypass (finding 2) → unwaived-assumed: 'assumed' alone is not a pass.
|
|
13
|
+
# 3. status-misassertion (finding 3) → status-misassertion: CI re-derives status; a
|
|
14
|
+
# self-reported status that does not match the bundle's own evidence is rejected.
|
|
15
|
+
# 4. waived-command-check (finding 4) → waiver-on-command-check: a command-backed
|
|
16
|
+
# (test_output) check cannot be waived.
|
|
17
|
+
# 5. ws3-old-style-bundle (AC6) → old all-test_output bundle FAILS the same way (exit 1,
|
|
18
|
+
# divergences) under the new reconciler as under the old one — no soundness regression.
|
|
19
|
+
# 6. fabricated-attestation (iteration-4, converged iteration-3 finding, both gates) → a
|
|
20
|
+
# fully self-consistent, hand-fabricated no-command 'security' claim+evidence+event
|
|
21
|
+
# triple (indistinguishable from a genuine attestation at the reconciler's own
|
|
22
|
+
# re-derivation layer) MUST still pass (exit 0 — blocking attestations at L0 would break
|
|
23
|
+
# every honest human-attestation use) BUT MUST be loudly, distinctly marked
|
|
24
|
+
# 'ATTESTED (not independently verifiable at L0)' plus the summary count line — never a
|
|
25
|
+
# quiet SESSION-LOCAL OK indistinguishable from a reconciled check. See ADR 0020 Residuals.
|
|
26
|
+
#
|
|
27
|
+
# Fresh-verify (Step 1) is a trivial pass so the proof is focused on the reconcile step; the
|
|
28
|
+
# manifest resolves from this repo's live run-baseline.sh registry (--repo-root "$ROOT").
|
|
29
|
+
# Deterministic, no model spend, self-cleaning.
|
|
30
|
+
# Usage: bash evals/integration/test_trust_reconcile_negatives.sh
|
|
31
|
+
|
|
32
|
+
set -uo pipefail
|
|
33
|
+
|
|
34
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
35
|
+
RECONCILE="$ROOT/scripts/ci/trust-reconcile.js"
|
|
36
|
+
FX="$ROOT/evals/fixtures/trust-reconcile-exploits"
|
|
37
|
+
WS3="$ROOT/evals/fixtures/trust-reconcile-ws3/ws3-bundle.json"
|
|
38
|
+
|
|
39
|
+
errors=0
|
|
40
|
+
_pass() { echo " PASS: $1"; }
|
|
41
|
+
_fail() { echo " FAIL: $1"; errors=$((errors + 1)); }
|
|
42
|
+
|
|
43
|
+
# run_case <label> <bundle> <needle>
|
|
44
|
+
# Asserts: reconciler exits non-zero AND stdout/stderr contains <needle>.
|
|
45
|
+
run_case() {
|
|
46
|
+
local label="$1" bundle="$2" needle="$3"
|
|
47
|
+
echo "=== $label ==="
|
|
48
|
+
if [[ ! -f "$bundle" ]]; then _fail "$label: fixture not found at $bundle"; return; fi
|
|
49
|
+
local out code
|
|
50
|
+
out="$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
51
|
+
node "$RECONCILE" --bundle "$bundle" --repo-root "$ROOT" 2>&1)"
|
|
52
|
+
code=$?
|
|
53
|
+
if [[ $code -ne 0 ]]; then
|
|
54
|
+
_pass "$label: reconciler exits non-zero ($code)"
|
|
55
|
+
else
|
|
56
|
+
_fail "$label: expected non-zero exit, got 0 — output: $out"
|
|
57
|
+
fi
|
|
58
|
+
if echo "$out" | grep -qF "$needle"; then
|
|
59
|
+
_pass "$label: emitted the expected divergence (\"$needle\")"
|
|
60
|
+
else
|
|
61
|
+
_fail "$label: expected divergence \"$needle\" not found — output: $out"
|
|
62
|
+
fi
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# 1. Reviewer's no-label exploit → not-run (test_output must reconcile against the manifest).
|
|
66
|
+
run_case "no-label-bypass (finding 1)" "$FX/no-label-bypass.json" \
|
|
67
|
+
"no manifest-matched execution.label"
|
|
68
|
+
|
|
69
|
+
# 2. Verifier's skip->assumed exploit → unwaived-assumed.
|
|
70
|
+
run_case "skip-assumed-bypass (finding 2)" "$FX/skip-assumed-bypass.json" \
|
|
71
|
+
"[unwaived-assumed]"
|
|
72
|
+
|
|
73
|
+
# 3. Status-misassertion exploit → status re-derived CI-side; asserted != derived.
|
|
74
|
+
run_case "status-misassertion (finding 3)" "$FX/status-misassertion.json" \
|
|
75
|
+
"[status-misassertion]"
|
|
76
|
+
|
|
77
|
+
# 4. Waived command-backed check → waiver-on-command-check.
|
|
78
|
+
run_case "waived-command-check (finding 4)" "$FX/waived-command-check.json" \
|
|
79
|
+
"[waiver-on-command-check]"
|
|
80
|
+
|
|
81
|
+
# 5. AC6 backward-compat regression: the real ws3-kit-dependencies-namespacing/trust.bundle
|
|
82
|
+
# (an old-style all-test_output bundle whose commands are not manifest-matched) FAILS the
|
|
83
|
+
# same way under the new reconciler as under the old one — same FAIL verdict, divergences
|
|
84
|
+
# present, no silent pass introduced.
|
|
85
|
+
echo "=== ws3 old-style bundle (AC6 backward compat) ==="
|
|
86
|
+
if [[ ! -f "$WS3" ]]; then
|
|
87
|
+
_fail "ws3 fixture not found at $WS3"
|
|
88
|
+
else
|
|
89
|
+
ws3_out="$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
90
|
+
node "$RECONCILE" --bundle "$WS3" --repo-root "$ROOT" 2>&1)"
|
|
91
|
+
ws3_code=$?
|
|
92
|
+
if [[ $ws3_code -ne 0 ]]; then
|
|
93
|
+
_pass "ws3 old-style bundle FAILS (exit $ws3_code) — same verdict as the old reconciler"
|
|
94
|
+
else
|
|
95
|
+
_fail "ws3 old-style bundle expected non-zero exit, got 0 — output: $ws3_out"
|
|
96
|
+
fi
|
|
97
|
+
if echo "$ws3_out" | grep -qF "[not-run]"; then
|
|
98
|
+
_pass "ws3 old-style bundle emits a not-run divergence (its test_output commands are not manifest-matched)"
|
|
99
|
+
else
|
|
100
|
+
_fail "ws3 old-style bundle expected a not-run divergence — output: $ws3_out"
|
|
101
|
+
fi
|
|
102
|
+
if echo "$ws3_out" | grep -qF "trust divergence"; then
|
|
103
|
+
_pass "ws3 old-style bundle emits trust divergence(s)"
|
|
104
|
+
else
|
|
105
|
+
_fail "ws3 old-style bundle expected trust divergence(s) — output: $ws3_out"
|
|
106
|
+
fi
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# 6. Fabricated-attestation (iteration-4): passes (exit 0) but MUST carry the loud ATTESTED
|
|
110
|
+
# marker and the summary count line — NOT the old quiet SESSION-LOCAL OK. This is a
|
|
111
|
+
# visibility assertion, not a divergence assertion (unlike run_case above): a fabricated
|
|
112
|
+
# self-consistent attestation bundle is, by construction, indistinguishable from a genuine
|
|
113
|
+
# one at this layer (see ADR 0020 Residuals) — the fix is disclosure, not a block.
|
|
114
|
+
echo "=== fabricated-attestation (iteration-4, converged finding) ==="
|
|
115
|
+
FAB="$FX/fabricated-attestation.json"
|
|
116
|
+
if [[ ! -f "$FAB" ]]; then
|
|
117
|
+
_fail "fabricated-attestation fixture not found at $FAB"
|
|
118
|
+
else
|
|
119
|
+
fab_out="$(TRUST_RECONCILE_COMMANDS="node -e 'process.exit(0)'" \
|
|
120
|
+
node "$RECONCILE" --bundle "$FAB" --repo-root "$ROOT" 2>&1)"
|
|
121
|
+
fab_code=$?
|
|
122
|
+
if [[ $fab_code -eq 0 ]]; then
|
|
123
|
+
_pass "fabricated-attestation bundle passes (exit 0) — attestations are not blocked at L0"
|
|
124
|
+
else
|
|
125
|
+
_fail "fabricated-attestation bundle expected exit 0, got $fab_code — output: $fab_out"
|
|
126
|
+
fi
|
|
127
|
+
if echo "$fab_out" | grep -qF "ATTESTED (not independently verifiable at L0): 'c-fabricated-security' (workflow.check.security) evidenceType=attestation"; then
|
|
128
|
+
_pass "fabricated-attestation bundle emits the loud ATTESTED marker (not a quiet SESSION-LOCAL OK)"
|
|
129
|
+
else
|
|
130
|
+
_fail "expected the ATTESTED (not independently verifiable at L0) marker — output: $fab_out"
|
|
131
|
+
fi
|
|
132
|
+
if echo "$fab_out" | grep -qF "1 attested claim(s) accepted without independent verification"; then
|
|
133
|
+
_pass "fabricated-attestation bundle emits the attested-claim summary count line"
|
|
134
|
+
else
|
|
135
|
+
_fail "expected the attested-claim summary count line — output: $fab_out"
|
|
136
|
+
fi
|
|
137
|
+
if echo "$fab_out" | grep -qF "SESSION-LOCAL OK"; then
|
|
138
|
+
_fail "fabricated-attestation bundle must NOT emit the old quiet SESSION-LOCAL OK line"
|
|
139
|
+
else
|
|
140
|
+
_pass "fabricated-attestation bundle does not emit the old quiet SESSION-LOCAL OK line"
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
echo ""
|
|
145
|
+
if [[ $errors -eq 0 ]]; then
|
|
146
|
+
echo "test_trust_reconcile_negatives: all checks passed."
|
|
147
|
+
exit 0
|
|
148
|
+
else
|
|
149
|
+
echo "test_trust_reconcile_negatives: $errors check(s) failed."
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
@@ -20,16 +20,16 @@ echo ""
|
|
|
20
20
|
global="$TMPDIR_EVAL/global"
|
|
21
21
|
repo_a="$TMPDIR_EVAL/repo-a"
|
|
22
22
|
repo_b="$TMPDIR_EVAL/repo-b"
|
|
23
|
-
mkdir -p "$repo_a/.flow-agents/alpha" "$repo_b/.flow-agents/beta"
|
|
23
|
+
mkdir -p "$repo_a/.kontourai/flow-agents/alpha" "$repo_b/.kontourai/flow-agents/beta"
|
|
24
24
|
|
|
25
|
-
cat > "$repo_a/.flow-agents/alpha/alpha--deliver.md" <<'MARKDOWN'
|
|
25
|
+
cat > "$repo_a/.kontourai/flow-agents/alpha/alpha--deliver.md" <<'MARKDOWN'
|
|
26
26
|
# Deliver Alpha
|
|
27
27
|
|
|
28
28
|
status: delivered
|
|
29
29
|
type: deliver
|
|
30
30
|
MARKDOWN
|
|
31
31
|
|
|
32
|
-
cat > "$repo_b/.flow-agents/beta/beta--verify.md" <<'MARKDOWN'
|
|
32
|
+
cat > "$repo_b/.kontourai/flow-agents/beta/beta--verify.md" <<'MARKDOWN'
|
|
33
33
|
# Verify Beta
|
|
34
34
|
|
|
35
35
|
status: failed
|
|
@@ -94,8 +94,8 @@ fi
|
|
|
94
94
|
|
|
95
95
|
discover_root="$TMPDIR_EVAL/discover-root"
|
|
96
96
|
repo_c="$discover_root/gamma"
|
|
97
|
-
mkdir -p "$repo_c/.flow-agents/gamma"
|
|
98
|
-
cat > "$repo_c/.flow-agents/gamma/gamma--deliver.md" <<'MARKDOWN'
|
|
97
|
+
mkdir -p "$repo_c/.kontourai/flow-agents/gamma"
|
|
98
|
+
cat > "$repo_c/.kontourai/flow-agents/gamma/gamma--deliver.md" <<'MARKDOWN'
|
|
99
99
|
# Deliver Gamma
|
|
100
100
|
|
|
101
101
|
status: delivered
|