@kontourai/flow-agents 3.1.0 → 3.2.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/workflows/ci.yml +4 -0
- package/CHANGELOG.md +17 -0
- package/build/src/cli/assignment-provider.d.ts +45 -0
- package/build/src/cli/assignment-provider.js +97 -12
- package/build/src/cli/workflow-sidecar.d.ts +14 -4
- package/build/src/cli/workflow-sidecar.js +100 -10
- package/context/contracts/assignment-provider-contract.md +1 -1
- package/context/contracts/execution-contract.md +78 -0
- package/context/scripts/hooks/config-protection.js +11 -4
- package/context/scripts/hooks/stop-goal-fit.js +259 -4
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +111 -0
- package/evals/ci/run-baseline.sh +2 -0
- package/evals/integration/test_checkpoint_signing.sh +4 -3
- package/evals/integration/test_gate_lockdown.sh +36 -0
- package/evals/integration/test_model_routing_escalation.sh +145 -0
- package/evals/integration/test_publish_delivery.sh +14 -6
- package/evals/integration/test_stop_hook_release.sh +552 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +170 -0
- package/evals/run.sh +6 -0
- package/evals/static/test_model_routing_hints.sh +107 -0
- package/kits/builder/skills/builder-shape/SKILL.md +10 -0
- package/kits/builder/skills/deliver/SKILL.md +52 -11
- package/kits/builder/skills/design-probe/SKILL.md +10 -0
- package/kits/builder/skills/execute-plan/SKILL.md +13 -0
- package/kits/builder/skills/fix-bug/SKILL.md +17 -0
- package/kits/builder/skills/idea-to-backlog/SKILL.md +10 -0
- package/kits/builder/skills/plan-work/SKILL.md +9 -0
- package/kits/builder/skills/pull-work/SKILL.md +10 -0
- package/kits/builder/skills/review-work/SKILL.md +11 -0
- package/kits/builder/skills/tdd-workflow/SKILL.md +17 -0
- package/kits/builder/skills/verify-work/SKILL.md +11 -0
- package/kits/knowledge/adapters/default-store/index.js +56 -15
- package/kits/knowledge/adapters/flow-runner/index.js +912 -16
- package/kits/knowledge/adapters/obsidian-store/index.js +29 -11
- package/kits/knowledge/adapters/shared/codec.js +124 -0
- package/kits/knowledge/docs/store-contract.md +405 -3
- package/kits/knowledge/evals/audit-freshness/suite.test.js +92 -1
- package/kits/knowledge/evals/consolidate-incremental/suite.test.js +494 -0
- package/kits/knowledge/evals/consolidation/suite.test.js +1 -1
- package/kits/knowledge/evals/contract-suite/suite.test.js +36 -0
- package/kits/knowledge/evals/freshness/suite.test.js +339 -0
- package/kits/knowledge/evals/inbound-references/suite.test.js +351 -0
- package/kits/knowledge/evals/retirement/suite.test.js +1 -1
- package/kits/knowledge/evals/supersede-propagation/suite.test.js +384 -0
- package/package.json +1 -1
- package/schemas/workflow-handoff.schema.json +6 -0
- package/scripts/ci/mint-attestation.js +33 -6
- package/scripts/ci/trust-reconcile.js +144 -26
- package/scripts/hooks/config-protection.js +11 -4
- package/scripts/hooks/stop-goal-fit.js +259 -4
- package/src/cli/assignment-provider.ts +110 -12
- package/src/cli/workflow-sidecar.ts +99 -10
|
@@ -749,6 +749,176 @@ else
|
|
|
749
749
|
_fail "push-event-failing-verify: expected the push-event no-op line -- output: $out7v"
|
|
750
750
|
fi
|
|
751
751
|
|
|
752
|
+
# 8. #379 per-session delivery paths — concurrent-delivery collision resolution.
|
|
753
|
+
# resolveDeliveryCandidates() now discovers delivery/<slug>/trust.bundle per-session dirs
|
|
754
|
+
# in addition to the flat path; discoverBundle() selects the candidate whose checkpoint
|
|
755
|
+
# attests THIS change (ancestor-or-equal, same bundleAttestsThisChange() semantics the flat
|
|
756
|
+
# staleness gate uses), ignoring stale siblings from OTHER concurrent sessions. A shared
|
|
757
|
+
# flat path guaranteed a git conflict between any two concurrent deliveries → a DIRTY PR →
|
|
758
|
+
# NO pull_request workflows → the required check silently never ran (field #330/#358/#378).
|
|
759
|
+
echo ""
|
|
760
|
+
echo "=== 8. #379 per-session delivery paths (concurrent-delivery collision) ==="
|
|
761
|
+
|
|
762
|
+
PERSESSION_TMPROOT="$(mktemp -d)"
|
|
763
|
+
# Chain per-session cleanup onto the existing DECLARED cleanup without clobbering the trap.
|
|
764
|
+
trap 'cleanup_declared; rm -rf "$PERSESSION_TMPROOT"' EXIT
|
|
765
|
+
|
|
766
|
+
# write_session_seal <repo_root> <slug> <label> <commit_sha>
|
|
767
|
+
# Writes delivery/<slug>/trust.bundle (a well-formed schemaVersion-5 bundle whose only
|
|
768
|
+
# claimed-pass command is <label>) + delivery/<slug>/trust.checkpoint.json (naming
|
|
769
|
+
# <commit_sha>) — the per-session analogue of write_fresh_bundle + write_stale_checkpoint.
|
|
770
|
+
write_session_seal() {
|
|
771
|
+
local repo_root="$1" slug="$2" label="$3" sha="$4"
|
|
772
|
+
local sdir="$repo_root/delivery/$slug"
|
|
773
|
+
mkdir -p "$sdir"
|
|
774
|
+
cat > "$sdir/trust.bundle" << EOF
|
|
775
|
+
{
|
|
776
|
+
"schemaVersion": 5,
|
|
777
|
+
"source": "test-fixture:per-session:$slug",
|
|
778
|
+
"claims": [
|
|
779
|
+
{
|
|
780
|
+
"id": "c1", "claimType": "workflow.check.build", "value": "pass", "status": "verified",
|
|
781
|
+
"subjectId": "$slug/build", "facet": "flow-agents.workflow", "subjectType": "workflow-check",
|
|
782
|
+
"fieldOrBehavior": "build", "createdAt": "2026-07-01T00:00:00Z", "updatedAt": "2026-07-01T00:00:00Z",
|
|
783
|
+
"impactLevel": "high", "verificationPolicyId": "policy:workflow.check.build"
|
|
784
|
+
}
|
|
785
|
+
],
|
|
786
|
+
"evidence": [
|
|
787
|
+
{
|
|
788
|
+
"id": "ev1", "claimId": "c1", "evidenceType": "test_output", "method": "validation",
|
|
789
|
+
"sourceRef": "$slug/command-log.jsonl", "excerptOrSummary": "build",
|
|
790
|
+
"observedAt": "2026-07-01T00:00:00Z", "collectedBy": "flow-agents/evidence-capture",
|
|
791
|
+
"passing": true,
|
|
792
|
+
"execution": { "runner": "bash", "label": "$label", "isError": false, "exitCode": 0 }
|
|
793
|
+
}
|
|
794
|
+
],
|
|
795
|
+
"policies": [], "events": []
|
|
796
|
+
}
|
|
797
|
+
EOF
|
|
798
|
+
printf '{"schema_version":"1.0","slug":"%s","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":{}}}' \
|
|
799
|
+
"$slug" "$sha" > "$sdir/trust.checkpoint.json"
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
FRESH_LABEL_8="node -e 'process.exit(0)'"
|
|
803
|
+
|
|
804
|
+
# 8a. OWNER WINS: two sibling session dirs — one OWNING (checkpoint commit_sha == this
|
|
805
|
+
# change's sha), one STALE (a different sha). The reconciler MUST pick the owner and
|
|
806
|
+
# reconcile it; the stale sibling is ignored, NOT a failure.
|
|
807
|
+
CASE8A="$PERSESSION_TMPROOT/collision-owner-wins"
|
|
808
|
+
write_session_seal "$CASE8A" "owning-session" "$FRESH_LABEL_8" "$OURS_SHA"
|
|
809
|
+
write_session_seal "$CASE8A" "stale-sibling" "echo stale-should-never-be-reconciled" "$STALE_SHA"
|
|
810
|
+
out8a="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_8" \
|
|
811
|
+
node "$RECONCILE" --repo-root "$CASE8A" 2>&1)"
|
|
812
|
+
code8a=$?
|
|
813
|
+
if [[ $code8a -eq 0 ]]; then
|
|
814
|
+
_pass "persession-owner-wins: reconciler exits 0 (owning per-session candidate selected, stale sibling ignored)"
|
|
815
|
+
else
|
|
816
|
+
_fail "persession-owner-wins: expected exit 0, got $code8a -- output: $out8a"
|
|
817
|
+
fi
|
|
818
|
+
if echo "$out8a" | grep -qF "selected delivery candidate delivery/owning-session/trust.bundle"; then
|
|
819
|
+
_pass "persession-owner-wins: emitted the #379 selection line naming the owning session dir"
|
|
820
|
+
else
|
|
821
|
+
_fail "persession-owner-wins: expected the #379 selection line for delivery/owning-session/trust.bundle -- output: $out8a"
|
|
822
|
+
fi
|
|
823
|
+
if echo "$out8a" | grep -qF "RECONCILED"; then
|
|
824
|
+
_pass "persession-owner-wins: Step 2 reconciled the owning candidate's claimed-pass command"
|
|
825
|
+
else
|
|
826
|
+
_fail "persession-owner-wins: expected RECONCILED -- output: $out8a"
|
|
827
|
+
fi
|
|
828
|
+
if echo "$out8a" | grep -qF "bundle-required-no-declared-marker"; then
|
|
829
|
+
_fail "persession-owner-wins: must NOT fail closed — an owning candidate exists"
|
|
830
|
+
else
|
|
831
|
+
_pass "persession-owner-wins: does not fail closed (owning candidate found)"
|
|
832
|
+
fi
|
|
833
|
+
|
|
834
|
+
# 8b. NO OWNER: two sibling session dirs, BOTH stale (neither attests this change), no
|
|
835
|
+
# DECLARED marker -> fail closed EXACTLY as bundle-absence, plus the #379 concurrency
|
|
836
|
+
# hint so the next agent can diagnose a collision rather than a plain stale bundle.
|
|
837
|
+
CASE8B="$PERSESSION_TMPROOT/collision-no-owner"
|
|
838
|
+
OTHER_STALE_SHA="1111111111111111111111111111111111111111"
|
|
839
|
+
write_session_seal "$CASE8B" "session-a" "echo a" "$STALE_SHA"
|
|
840
|
+
write_session_seal "$CASE8B" "session-b" "echo b" "$OTHER_STALE_SHA"
|
|
841
|
+
out8b="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_8" \
|
|
842
|
+
node "$RECONCILE" --repo-root "$CASE8B" 2>&1)"
|
|
843
|
+
code8b=$?
|
|
844
|
+
if [[ $code8b -ne 0 ]]; then
|
|
845
|
+
_pass "persession-no-owner: reconciler exits non-zero ($code8b) -- no candidate attests this change, fail closed"
|
|
846
|
+
else
|
|
847
|
+
_fail "persession-no-owner: expected non-zero exit, got 0 -- output: $out8b"
|
|
848
|
+
fi
|
|
849
|
+
if echo "$out8b" | grep -qF "none attests this change $OURS_SHA"; then
|
|
850
|
+
_pass "persession-no-owner: emitted the #379 concurrency hint (none attests this change)"
|
|
851
|
+
else
|
|
852
|
+
_fail "persession-no-owner: expected the #379 'none attests this change' hint -- output: $out8b"
|
|
853
|
+
fi
|
|
854
|
+
if echo "$out8b" | grep -qF "bundle-required-no-declared-marker"; then
|
|
855
|
+
_pass "persession-no-owner: emitted 'bundle-required-no-declared-marker' (same fail-closed path as bundle-absence)"
|
|
856
|
+
else
|
|
857
|
+
_fail "persession-no-owner: expected 'bundle-required-no-declared-marker' -- output: $out8b"
|
|
858
|
+
fi
|
|
859
|
+
|
|
860
|
+
# 8c. BACK-COMPAT COEXISTENCE: a flat legacy owner (delivery/trust.bundle) alongside a stale
|
|
861
|
+
# per-session sibling -> the flat owner is still selected and reconciled (the flat path
|
|
862
|
+
# stays supported; per-session discovery does not break it).
|
|
863
|
+
CASE8C="$PERSESSION_TMPROOT/flat-owner-persession-stale"
|
|
864
|
+
write_fresh_bundle "$CASE8C" "$FRESH_LABEL_8" # flat delivery/trust.bundle
|
|
865
|
+
write_stale_checkpoint "$CASE8C" "$OURS_SHA" # flat delivery/trust.checkpoint.json (owns)
|
|
866
|
+
write_session_seal "$CASE8C" "stale-session" "echo ignored" "$STALE_SHA" # per-session stale sibling
|
|
867
|
+
out8c="$(TRUST_RECONCILE_SHA="$OURS_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_8" \
|
|
868
|
+
node "$RECONCILE" --repo-root "$CASE8C" 2>&1)"
|
|
869
|
+
code8c=$?
|
|
870
|
+
if [[ $code8c -eq 0 ]]; then
|
|
871
|
+
_pass "flat-owner-coexist: reconciler exits 0 (flat legacy owner selected despite a stale per-session sibling)"
|
|
872
|
+
else
|
|
873
|
+
_fail "flat-owner-coexist: expected exit 0, got $code8c -- output: $out8c"
|
|
874
|
+
fi
|
|
875
|
+
if echo "$out8c" | grep -qF "RECONCILED"; then
|
|
876
|
+
_pass "flat-owner-coexist: Step 2 reconciled the flat owner (back-compat preserved)"
|
|
877
|
+
else
|
|
878
|
+
_fail "flat-owner-coexist: expected RECONCILED -- output: $out8c"
|
|
879
|
+
fi
|
|
880
|
+
|
|
881
|
+
# 8d. PREFER-NEWEST among multiple OWNING candidates (the merge-commit-repo / concurrent-PR
|
|
882
|
+
# coexistence case). An inherited FLAT bundle can attest a REAL ANCESTOR of this change
|
|
883
|
+
# (committed on the trunk before this branch), AND this session's per-session bundle attests
|
|
884
|
+
# a NEWER ancestor. "First-fresh-wins" would wrongly pick the stale flat bundle because it
|
|
885
|
+
# sorts first; the reconciler must instead pick the NEWEST-owning candidate (the per-session
|
|
886
|
+
# one). Uses a REAL git repo so `git merge-base --is-ancestor` resolves the parent→child
|
|
887
|
+
# relationship (synthetic shas cannot exercise the ancestor comparison).
|
|
888
|
+
NEWEST_REPO="$PERSESSION_TMPROOT/prefer-newest-git"
|
|
889
|
+
mkdir -p "$NEWEST_REPO"
|
|
890
|
+
git -C "$NEWEST_REPO" init -q
|
|
891
|
+
git -C "$NEWEST_REPO" config user.email "test@example.com"
|
|
892
|
+
git -C "$NEWEST_REPO" config user.name "Test"
|
|
893
|
+
echo "base" > "$NEWEST_REPO/f.txt"; git -C "$NEWEST_REPO" add f.txt; git -C "$NEWEST_REPO" commit -q -m "base (inherited flat seal sealed here)"
|
|
894
|
+
FLAT_ANCESTOR_SHA="$(git -C "$NEWEST_REPO" rev-parse HEAD)"
|
|
895
|
+
echo "delivery" >> "$NEWEST_REPO/f.txt"; git -C "$NEWEST_REPO" add f.txt; git -C "$NEWEST_REPO" commit -q -m "this session's delivery commit"
|
|
896
|
+
PERSESSION_OWNER_SHA="$(git -C "$NEWEST_REPO" rev-parse HEAD)"
|
|
897
|
+
echo "head" >> "$NEWEST_REPO/f.txt"; git -C "$NEWEST_REPO" add f.txt; git -C "$NEWEST_REPO" commit -q -m "seal commit (HEAD)"
|
|
898
|
+
NEWEST_HEAD_SHA="$(git -C "$NEWEST_REPO" rev-parse HEAD)"
|
|
899
|
+
# Flat owner attesting the OLD ancestor; per-session owner attesting the NEWER ancestor.
|
|
900
|
+
write_fresh_bundle "$NEWEST_REPO" "$FRESH_LABEL_8" # flat delivery/trust.bundle
|
|
901
|
+
write_stale_checkpoint "$NEWEST_REPO" "$FLAT_ANCESTOR_SHA" # flat checkpoint -> OLD ancestor
|
|
902
|
+
write_session_seal "$NEWEST_REPO" "this-session" "$FRESH_LABEL_8" "$PERSESSION_OWNER_SHA" # per-session -> NEWER ancestor
|
|
903
|
+
out8d="$(TRUST_RECONCILE_SHA="$NEWEST_HEAD_SHA" TRUST_RECONCILE_COMMANDS="$FRESH_LABEL_8" \
|
|
904
|
+
node "$RECONCILE" --repo-root "$NEWEST_REPO" 2>&1)"
|
|
905
|
+
code8d=$?
|
|
906
|
+
if [[ $code8d -eq 0 ]]; then
|
|
907
|
+
_pass "prefer-newest: reconciler exits 0 (both flat and per-session own; newest selected)"
|
|
908
|
+
else
|
|
909
|
+
_fail "prefer-newest: expected exit 0, got $code8d -- output: $out8d"
|
|
910
|
+
fi
|
|
911
|
+
if echo "$out8d" | grep -qF "selected delivery candidate delivery/this-session/trust.bundle"; then
|
|
912
|
+
_pass "prefer-newest: selected the NEWER per-session bundle over the older inherited flat bundle"
|
|
913
|
+
else
|
|
914
|
+
_fail "prefer-newest: expected the per-session bundle to be selected (newest-owning) -- output: $out8d"
|
|
915
|
+
fi
|
|
916
|
+
if echo "$out8d" | grep -qF "owning, newest wins"; then
|
|
917
|
+
_pass "prefer-newest: emitted the 'owning, newest wins' selection detail"
|
|
918
|
+
else
|
|
919
|
+
_fail "prefer-newest: expected 'owning, newest wins' in the selection line -- output: $out8d"
|
|
920
|
+
fi
|
|
921
|
+
|
|
752
922
|
echo ""
|
|
753
923
|
if [[ $errors -eq 0 ]]; then
|
|
754
924
|
echo "test_trust_reconcile_negatives: all checks passed."
|
package/evals/run.sh
CHANGED
|
@@ -148,6 +148,8 @@ run_static() {
|
|
|
148
148
|
bash "$EVAL_DIR/static/test_unit_helpers.sh" || result=1
|
|
149
149
|
echo ""
|
|
150
150
|
bash "$EVAL_DIR/static/test_knowledge_providers.sh" || result=1
|
|
151
|
+
echo ""
|
|
152
|
+
bash "$EVAL_DIR/static/test_model_routing_hints.sh" || result=1
|
|
151
153
|
return $result
|
|
152
154
|
}
|
|
153
155
|
|
|
@@ -201,6 +203,8 @@ run_integration() {
|
|
|
201
203
|
echo ""
|
|
202
204
|
bash "$EVAL_DIR/integration/test_assignment_provider_github.sh" || result=1
|
|
203
205
|
echo ""
|
|
206
|
+
bash "$EVAL_DIR/integration/test_stop_hook_release.sh" || result=1
|
|
207
|
+
echo ""
|
|
204
208
|
bash "$EVAL_DIR/integration/test_pull_work_assignment_join.sh" || result=1
|
|
205
209
|
echo ""
|
|
206
210
|
bash "$EVAL_DIR/integration/test_ensure_session_ownership_guard.sh" || result=1
|
|
@@ -282,6 +286,8 @@ run_integration() {
|
|
|
282
286
|
echo ""
|
|
283
287
|
bash "$EVAL_DIR/integration/test_kit_identity_trust.sh" || result=1
|
|
284
288
|
echo ""
|
|
289
|
+
bash "$EVAL_DIR/integration/test_model_routing_escalation.sh" || result=1
|
|
290
|
+
echo ""
|
|
285
291
|
bash "$EVAL_DIR/acceptance/prove-capture-teeth-declared.sh" || result=1
|
|
286
292
|
return $result
|
|
287
293
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# test_model_routing_hints.sh — #376
|
|
3
|
+
# Asserts that every delegating Builder Kit skill carries a per-step model-role
|
|
4
|
+
# hint pointing at the execution contract (AC1/R1), that the execution contract
|
|
5
|
+
# documents the escalate-on-gate-failure ladder (R2) and the Goodhart guard
|
|
6
|
+
# (R3, review/verify tier >= worker tier), and that review/verify skill hints
|
|
7
|
+
# encode that guard.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
11
|
+
cd "$ROOT_DIR"
|
|
12
|
+
|
|
13
|
+
SKILLS="kits/builder/skills"
|
|
14
|
+
CONTRACT="context/contracts/execution-contract.md"
|
|
15
|
+
POINTER="context/contracts/execution-contract.md"
|
|
16
|
+
|
|
17
|
+
pass() { echo "PASS: $1"; }
|
|
18
|
+
fail() { echo "FAIL: $1" >&2; exit 1; }
|
|
19
|
+
|
|
20
|
+
require_contains() {
|
|
21
|
+
local file="$1" pattern="$2" label="$3"
|
|
22
|
+
grep -Fq -- "$pattern" "$file" || fail "$label ($file)"
|
|
23
|
+
pass "$label"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
skill_has_routing_hint() {
|
|
27
|
+
# A delegating skill must have a Model Routing section that points at the
|
|
28
|
+
# execution contract's routing section — the single consumption instruction.
|
|
29
|
+
local skill="$1"
|
|
30
|
+
local file="$SKILLS/$skill/SKILL.md"
|
|
31
|
+
[[ -f "$file" ]] || fail "$skill SKILL.md missing"
|
|
32
|
+
grep -q "^## Model Routing" "$file" || fail "$skill has no '## Model Routing' section"
|
|
33
|
+
grep -Fq "$POINTER" "$file" || fail "$skill routing hint does not point at the execution contract"
|
|
34
|
+
pass "$skill has a Model Routing hint pointing at the execution contract"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
skill_names_role() {
|
|
38
|
+
local skill="$1" role="$2"
|
|
39
|
+
local file="$SKILLS/$skill/SKILL.md"
|
|
40
|
+
grep -Fq "$role" "$file" || fail "$skill does not name role '$role'"
|
|
41
|
+
pass "$skill names role '$role'"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# ── R1/AC1: the delegating Builder Kit skills and the role each must name ──────
|
|
45
|
+
# Design tier: shaping / probing / planning need design latitude.
|
|
46
|
+
for s in builder-shape design-probe idea-to-backlog plan-work; do
|
|
47
|
+
skill_has_routing_hint "$s"
|
|
48
|
+
skill_names_role "$s" "delegate-design"
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
# Mechanical tier: board selection / sync / scan bookkeeping.
|
|
52
|
+
skill_has_routing_hint "pull-work"
|
|
53
|
+
skill_names_role "pull-work" "delegate-mechanical"
|
|
54
|
+
|
|
55
|
+
# Implementation tier: worker slices.
|
|
56
|
+
skill_has_routing_hint "execute-plan"
|
|
57
|
+
skill_names_role "execute-plan" "delegate-implementation"
|
|
58
|
+
|
|
59
|
+
# Review / verify: default implementation, subject to the Goodhart guard (R3).
|
|
60
|
+
for s in review-work verify-work; do
|
|
61
|
+
skill_has_routing_hint "$s"
|
|
62
|
+
skill_names_role "$s" "delegate-implementation"
|
|
63
|
+
done
|
|
64
|
+
|
|
65
|
+
# Multi-delegate orchestrators carry a role table covering all three tiers.
|
|
66
|
+
for s in deliver fix-bug tdd-workflow; do
|
|
67
|
+
skill_has_routing_hint "$s"
|
|
68
|
+
skill_names_role "$s" "delegate-mechanical"
|
|
69
|
+
skill_names_role "$s" "delegate-implementation"
|
|
70
|
+
skill_names_role "$s" "delegate-design"
|
|
71
|
+
done
|
|
72
|
+
|
|
73
|
+
# ── Drift guard: any builder skill that names a tool-* delegate must be a
|
|
74
|
+
# registered delegating skill above (i.e., must carry a Model Routing hint).
|
|
75
|
+
DELEGATING="builder-shape design-probe idea-to-backlog plan-work pull-work execute-plan review-work verify-work deliver fix-bug tdd-workflow"
|
|
76
|
+
for file in "$SKILLS"/*/SKILL.md; do
|
|
77
|
+
skill="$(basename "$(dirname "$file")")"
|
|
78
|
+
if grep -qE "tool-(worker|planner|code-reviewer|security-reviewer|verifier|playwright)" "$file"; then
|
|
79
|
+
case " $DELEGATING " in
|
|
80
|
+
*" $skill "*) : ;;
|
|
81
|
+
*) fail "skill '$skill' names a tool-* delegate but has no registered Model Routing hint (register it in test_model_routing_hints.sh and add the hint)" ;;
|
|
82
|
+
esac
|
|
83
|
+
fi
|
|
84
|
+
done
|
|
85
|
+
pass "every tool-* delegating builder skill is registered with a Model Routing hint"
|
|
86
|
+
|
|
87
|
+
# ── R3/AC3: review + verify skill hints encode the Goodhart guard ─────────────
|
|
88
|
+
for s in review-work verify-work; do
|
|
89
|
+
file="$SKILLS/$s/SKILL.md"
|
|
90
|
+
grep -Fq "Goodhart guard" "$file" || fail "$s hint does not name the Goodhart guard"
|
|
91
|
+
grep -Fq "never" "$file" || fail "$s hint does not state the never-downgrade rule"
|
|
92
|
+
grep -Fiq "below" "$file" || fail "$s hint does not say review/verify never resolves below the checked work"
|
|
93
|
+
pass "$s hint encodes the Goodhart guard (never below the checked-work tier)"
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
# ── R2/AC2 + R3/AC3: the execution contract documents ladder + guard + record ─
|
|
97
|
+
require_contains "$CONTRACT" "delegate-mechanical < delegate-implementation < delegate-design" "contract defines the tier ladder ordering"
|
|
98
|
+
require_contains "$CONTRACT" "Escalation on gate failure" "contract documents the escalation ladder"
|
|
99
|
+
require_contains "$CONTRACT" "one tier higher" "contract escalates the fix one tier higher on gate failure"
|
|
100
|
+
require_contains "$CONTRACT" "Goodhart guard" "contract documents the Goodhart guard"
|
|
101
|
+
require_contains "$CONTRACT" "greater than or equal to" "contract states review/verify tier >= checked-work tier"
|
|
102
|
+
# R4: contract names the additive per-delegation recording shape (#349 consumable).
|
|
103
|
+
require_contains "$CONTRACT" "Routing decisions in the run artifact" "contract documents routing-in-artifact recording"
|
|
104
|
+
require_contains "$CONTRACT" "--escalated-from" "contract records escalations with --escalated-from"
|
|
105
|
+
require_contains "$CONTRACT" "flow-agents#349" "contract ties routing records to the #349 economics record"
|
|
106
|
+
|
|
107
|
+
echo "Model routing hint + ladder + Goodhart guard static checks passed."
|
|
@@ -22,6 +22,16 @@ Invoke the Builder Kit `shape` flow for raw product ideas, vague build goals, cu
|
|
|
22
22
|
- Compatibility: Direct `idea-to-backlog` usage remains valid and should behave exactly as described in `kits/builder/skills/idea-to-backlog/SKILL.md`.
|
|
23
23
|
- Primitive recovery: if a user invokes `idea-to-backlog` or another primitive with missing shaping context and appears to want the product flow, explain that Builder Kit shape is the entry point and offer to route there.
|
|
24
24
|
|
|
25
|
+
## Model Routing
|
|
26
|
+
|
|
27
|
+
When this skill delegates its shaping work (to `idea-to-backlog` / `design-probe`
|
|
28
|
+
alignment), resolve the `delegate-design` role from `.datum/config.json`
|
|
29
|
+
(`npx @kontourai/datum resolve delegate-design --json`) and pass the resolved
|
|
30
|
+
model explicitly — shaping needs design latitude, so it routes to the design
|
|
31
|
+
tier. See `context/contracts/execution-contract.md` § Delegation: Model Routing.
|
|
32
|
+
Fallback: inherit the session model when datum/config is absent, noted in the
|
|
33
|
+
artifact.
|
|
34
|
+
|
|
25
35
|
## Invocation
|
|
26
36
|
|
|
27
37
|
Use this skill when the user says things like:
|
|
@@ -32,11 +32,16 @@ source of truth for the mapping):
|
|
|
32
32
|
|---|---|
|
|
33
33
|
| tool-worker | `delegate-mechanical` for fully-specified mechanical tasks, `delegate-implementation` for precisely-planned implementation, `delegate-design` when the task needs design latitude |
|
|
34
34
|
| tool-planner | `delegate-design` |
|
|
35
|
-
| tool-code-reviewer / tool-security-reviewer | `delegate-implementation` |
|
|
36
|
-
| tool-verifier / tool-playwright | `delegate-implementation` |
|
|
35
|
+
| tool-code-reviewer / tool-security-reviewer | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
36
|
+
| tool-verifier / tool-playwright | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
and
|
|
38
|
+
On a review/verify gate failure, re-dispatch the fix one tier higher on the
|
|
39
|
+
ladder and record the escalation in the session artifact via
|
|
40
|
+
`record-agent-event --kind escalation --role <higher> --escalated-from <lower>`
|
|
41
|
+
(see `context/contracts/execution-contract.md` § Escalation on gate failure and
|
|
42
|
+
§ Routing decisions in the run artifact). If datum or the config is absent, fall
|
|
43
|
+
back to the runtime's inherited model and note the fallback in the session
|
|
44
|
+
artifact.
|
|
40
45
|
|
|
41
46
|
## Orchestrator Rule
|
|
42
47
|
|
|
@@ -236,23 +241,59 @@ Record the final local state with `advance-state`. Use `status: verified` only w
|
|
|
236
241
|
After review, verification, evidence, and Goal Fit are clean for the same diff:
|
|
237
242
|
|
|
238
243
|
1. Confirm the working tree contains only verified scope.
|
|
239
|
-
2. Publish the session trust bundle
|
|
244
|
+
2. Publish the session trust bundle so the CI trust-reconcile job can verify what the agent claimed. `record-release` (via the sidecar writer) does this automatically (best-effort). To publish or re-publish explicitly:
|
|
240
245
|
|
|
241
246
|
```bash
|
|
242
247
|
npm run workflow:sidecar -- publish-delivery .kontourai/flow-agents/<slug>
|
|
243
248
|
```
|
|
244
249
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
250
|
+
**#379 — per-session delivery paths.** `publishDelivery()` writes to a PER-SESSION path
|
|
251
|
+
`delivery/<slug>/trust.bundle` (+ `trust.checkpoint.json` companions), where `<slug>` is
|
|
252
|
+
your session artifact dir's basename — NOT the old shared flat `delivery/trust.bundle`.
|
|
253
|
+
This is deliberate: a shared path guaranteed a git merge conflict between ANY two
|
|
254
|
+
concurrent deliveries, and a conflicting PR gets no CI (see the loud callout below). The
|
|
255
|
+
CI reconciler discovers both the flat (back-compat) and per-session layouts and selects
|
|
256
|
+
the NEWEST candidate whose checkpoint attests THIS change by commit ancestry — so an older
|
|
257
|
+
inherited bundle that also happens to be an ancestor of your change is ignored in favour of
|
|
258
|
+
your fresh one, and stale siblings from other sessions are ignored. `publishDelivery()`
|
|
259
|
+
also prunes inherited per-session sibling seal dirs (unique-named, never a cross-PR
|
|
260
|
+
conflict) so `delivery/` stays small; it deliberately does NOT delete the shared flat
|
|
261
|
+
`delivery/trust.bundle` legacy path (a concurrent PR may still seal there, and deleting it
|
|
262
|
+
would cause the DIRTY→no-CI conflict in the callout below).
|
|
263
|
+
|
|
264
|
+
Then force-stage the per-session trust dir for the delivery commit. It is gitignored by
|
|
265
|
+
default (runtime artifacts written on every local delivery) — `-f` commits it
|
|
266
|
+
deliberately into THIS delivery PR so CI's trust-reconcile job can reconcile the
|
|
267
|
+
session's claims against fresh CI results:
|
|
249
268
|
|
|
250
269
|
```bash
|
|
251
|
-
git add -f delivery
|
|
270
|
+
git add -f delivery/<slug>/
|
|
252
271
|
```
|
|
253
272
|
|
|
254
|
-
|
|
273
|
+
(If `publishDelivery()` pruned a superseded per-session SIBLING dir, stage that deletion
|
|
274
|
+
too — `git add -A delivery/` after the force-add. Do NOT hand-delete the flat
|
|
275
|
+
`delivery/trust.bundle` in a delivery PR while other PRs may still seal to it.)
|
|
276
|
+
|
|
277
|
+
3. Commit the verified diff, including the force-added `delivery/<slug>/` trust artifacts.
|
|
255
278
|
4. Push the branch.
|
|
279
|
+
|
|
280
|
+
> **⚠ LOUD FAILURE MODE — a DIRTY PR gets NO CI, silently (#335/#379).** If `main` moves
|
|
281
|
+
> under your open PR and produces a merge conflict, GitHub marks the PR `DIRTY` and
|
|
282
|
+
> **schedules NO `pull_request` workflows for it** — zero checks, no error, nothing. The
|
|
283
|
+
> required **Trust Reconcile** gate then silently never runs, and the PR sits unbuildable
|
|
284
|
+
> looking like "CI vanished" rather than "conflict." Per-session delivery paths (#379)
|
|
285
|
+
> remove the STRUCTURAL cause for delivery-artifact conflicts (concurrent seals no longer
|
|
286
|
+
> share a file), but ANY other same-file conflict with `main` can still trigger it.
|
|
287
|
+
> **Diagnose it explicitly — do not assume a missing required check means "not run yet":**
|
|
288
|
+
>
|
|
289
|
+
> ```bash
|
|
290
|
+
> gh pr view <pr> --json mergeStateStatus,mergeable,statusCheckRollup
|
|
291
|
+
> ```
|
|
292
|
+
>
|
|
293
|
+
> `mergeStateStatus: DIRTY` (or `CONFLICTING`) with an ABSENT Trust Reconcile check is the
|
|
294
|
+
> signature. Fix by rebasing/merging `main` to clear the conflict and re-pushing — that
|
|
295
|
+
> re-triggers the `pull_request` workflows. A green-looking PR with the required gate simply
|
|
296
|
+
> MISSING is not "pending"; it is this failure mode until proven otherwise.
|
|
256
297
|
5. Open or update the provider change record with issue links, closing refs, evidence links, and verification summary, or record an explicit no-provider-change reason.
|
|
257
298
|
6. Wait for provider checks/CI or record missing checks as `NOT_VERIFIED`.
|
|
258
299
|
7. Record the gate claim for the Builder Kit `pr-open` step immediately after the PR is opened or updated:
|
|
@@ -24,6 +24,16 @@ This skill is modeled after Matt Pocock's `grill-me`: interview the user relentl
|
|
|
24
24
|
- Stop when shared understanding exists, or when the remaining uncertainty is explicitly recorded as an accepted gap.
|
|
25
25
|
- Do not silently convert uncertainty into implementation work.
|
|
26
26
|
|
|
27
|
+
## Model Routing
|
|
28
|
+
|
|
29
|
+
When this skill delegates its probing/alignment work, resolve the
|
|
30
|
+
`delegate-design` role from `.datum/config.json`
|
|
31
|
+
(`npx @kontourai/datum resolve delegate-design --json`) and pass the resolved
|
|
32
|
+
model explicitly — probing a goal, design, or recovery path needs design
|
|
33
|
+
latitude, so it routes to the design tier. See
|
|
34
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing. Fallback:
|
|
35
|
+
inherit the session model when datum/config is absent, noted in the artifact.
|
|
36
|
+
|
|
27
37
|
## When To Use
|
|
28
38
|
|
|
29
39
|
Use this skill for:
|
|
@@ -13,6 +13,19 @@ Plan artifact in, implemented code out. Fans out to tool-worker subagents in par
|
|
|
13
13
|
|---|---|
|
|
14
14
|
| tool-worker | Implementation per task spec (up to 4 parallel) |
|
|
15
15
|
|
|
16
|
+
## Model Routing
|
|
17
|
+
|
|
18
|
+
Worker slices (`tool-worker`) route by task shape: `delegate-implementation` for
|
|
19
|
+
precisely-planned implementation (the default), `delegate-mechanical` for
|
|
20
|
+
fully-specified mechanical slices (issue sync, doc/scan bookkeeping),
|
|
21
|
+
`delegate-design` when a slice genuinely needs design latitude. Resolve the role
|
|
22
|
+
from `.datum/config.json` (`npx @kontourai/datum resolve <role> --json`) and pass
|
|
23
|
+
the model explicitly. On a review/verify gate failure of a slice, re-dispatch its
|
|
24
|
+
fix one tier higher and record the escalation. See
|
|
25
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing (and
|
|
26
|
+
§ Escalation on gate failure). Fallback: inherit the session model when
|
|
27
|
+
datum/config is absent, noted in the artifact.
|
|
28
|
+
|
|
16
29
|
## Orchestrator Rule
|
|
17
30
|
|
|
18
31
|
You do not write source files. You read the plan artifact, fan out tasks to tool-worker, and update the session file between waves.
|
|
@@ -20,6 +20,23 @@ Inherited from primitives + diagnosis:
|
|
|
20
20
|
| tool-verifier | verify-work |
|
|
21
21
|
| tool-playwright | diagnosis (reproduce) + verify-work |
|
|
22
22
|
|
|
23
|
+
## Model Routing
|
|
24
|
+
|
|
25
|
+
Delegates are spawned with an explicit model override resolved from
|
|
26
|
+
`.datum/config.json` via `npx @kontourai/datum resolve <role> --json` — see
|
|
27
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing:
|
|
28
|
+
|
|
29
|
+
| Delegate | Role |
|
|
30
|
+
|---|---|
|
|
31
|
+
| tool-worker | `delegate-mechanical` for fully-specified mechanical slices, `delegate-implementation` for precisely-planned implementation, `delegate-design` when a slice needs design latitude |
|
|
32
|
+
| tool-planner | `delegate-design` |
|
|
33
|
+
| tool-code-reviewer / tool-security-reviewer | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
34
|
+
| tool-verifier / tool-playwright | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
35
|
+
|
|
36
|
+
On a review/verify gate failure, re-dispatch the fix one tier higher and record
|
|
37
|
+
the escalation (contract § Escalation on gate failure). Fallback: inherit the
|
|
38
|
+
session model when datum/config is absent, noted in the artifact.
|
|
39
|
+
|
|
23
40
|
## Orchestrator Rule
|
|
24
41
|
|
|
25
42
|
You never use `read`, `glob`, `grep`, or `code` on source files. All codebase analysis goes through tool-planner. All review goes through review-work. All verification goes through tool-verifier or tool-playwright.
|
|
@@ -21,6 +21,16 @@ Convert raw ideas into shaped, prioritized, executable backlog without starting
|
|
|
21
21
|
- Keep separate ideas separate until a shared outcome, hard dependency, or sequencing reason justifies bundling them.
|
|
22
22
|
- Push back when the user blends unrelated ideas, and ask them to justify why the ideas belong together before shaping bundled work.
|
|
23
23
|
|
|
24
|
+
## Model Routing
|
|
25
|
+
|
|
26
|
+
When this skill delegates its shaping work (opportunity review, option
|
|
27
|
+
exploration, slicing), resolve the `delegate-design` role from
|
|
28
|
+
`.datum/config.json` (`npx @kontourai/datum resolve delegate-design --json`) and
|
|
29
|
+
pass the resolved model explicitly — shaping a raw idea into a backlog needs
|
|
30
|
+
design latitude, so it routes to the design tier. See
|
|
31
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing. Fallback:
|
|
32
|
+
inherit the session model when datum/config is absent, noted in the artifact.
|
|
33
|
+
|
|
24
34
|
## Artifact Contract
|
|
25
35
|
|
|
26
36
|
Create or update `.kontourai/flow-agents/<slug>/<slug>--idea-to-backlog.md` with:
|
|
@@ -13,6 +13,15 @@ Goal + directory in, structured plan artifact out. Pure planning primitive.
|
|
|
13
13
|
|---|---|
|
|
14
14
|
| tool-planner | Codebase analysis, structured execution plan, writes plan artifact |
|
|
15
15
|
|
|
16
|
+
## Model Routing
|
|
17
|
+
|
|
18
|
+
Planning delegation (`tool-planner`) resolves the `delegate-design` role from
|
|
19
|
+
`.datum/config.json` (`npx @kontourai/datum resolve delegate-design --json`) and
|
|
20
|
+
passes the resolved model explicitly — turning a goal into a plan needs design
|
|
21
|
+
latitude, so it routes to the design tier. See
|
|
22
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing. Fallback:
|
|
23
|
+
inherit the session model when datum/config is absent, noted in the artifact.
|
|
24
|
+
|
|
16
25
|
## Orchestrator Rule
|
|
17
26
|
|
|
18
27
|
You do not read source files. You delegate to tool-planner and read the artifact it produces.
|
|
@@ -26,6 +26,16 @@ Select ready backlog work and prepare a bounded execution handoff without implem
|
|
|
26
26
|
- Every pull-work artifact must correlate to selected backlog refs, shepherding refs for active PRs/sidecars/issues being finished before new work, or `backlog_gap=true` with a route to `idea-to-backlog`; direct audits with no new selection must record `shepherding_item_ids` or `backlog_gap`, not free-floating implementation scope.
|
|
27
27
|
- A stale broad continuation instruction, such as "keep going", "pick up the next two", or "continue after merge", may allow queue inspection but must not bypass per-item pickup Probe evidence.
|
|
28
28
|
|
|
29
|
+
## Model Routing
|
|
30
|
+
|
|
31
|
+
Board selection, WIP/shepherding scans, dependency joins, liveness preflight, and
|
|
32
|
+
issue-sync-style bookkeeping are fully-specified mechanical work: when this skill
|
|
33
|
+
delegates them, resolve the `delegate-mechanical` role from `.datum/config.json`
|
|
34
|
+
(`npx @kontourai/datum resolve delegate-mechanical --json`) and pass the resolved
|
|
35
|
+
model explicitly. See `context/contracts/execution-contract.md` § Delegation:
|
|
36
|
+
Model Routing. Fallback: inherit the session model when datum/config is absent,
|
|
37
|
+
noted in the artifact.
|
|
38
|
+
|
|
29
39
|
## Inputs
|
|
30
40
|
|
|
31
41
|
- Repository or working directory.
|
|
@@ -29,6 +29,17 @@ Keeping them separate makes failures route cleanly:
|
|
|
29
29
|
| tool-dependencies-updater | Dependency review when package manifests, dependency manifests, package manager config, or lockfiles change |
|
|
30
30
|
| configured architecture/domain/IaC/policy reviewer | Optional reviewer when the project or user configures one |
|
|
31
31
|
|
|
32
|
+
## Model Routing
|
|
33
|
+
|
|
34
|
+
Review roles never resolve **below** the tier of the work they check (Goodhart
|
|
35
|
+
guard): default `delegate-implementation`, raised to match or exceed the tier
|
|
36
|
+
that produced the work under review — a reviewer of `delegate-design` work
|
|
37
|
+
resolves `delegate-design` or `orchestrator`, never a cheaper tier. Resolve the
|
|
38
|
+
role from `.datum/config.json` (`npx @kontourai/datum resolve <role> --json`) and
|
|
39
|
+
pass the model explicitly. See `context/contracts/execution-contract.md`
|
|
40
|
+
§ Delegation: Model Routing and § Goodhart guard. Fallback: inherit the session
|
|
41
|
+
model when datum/config is absent, noted in the artifact.
|
|
42
|
+
|
|
32
43
|
## Shared Contracts
|
|
33
44
|
|
|
34
45
|
Follow:
|
|
@@ -25,6 +25,23 @@ Same as deliver (inherited from primitives):
|
|
|
25
25
|
| tool-verifier | verify-work (with coverage check) |
|
|
26
26
|
| tool-playwright | verify-work (if UI) |
|
|
27
27
|
|
|
28
|
+
## Model Routing
|
|
29
|
+
|
|
30
|
+
Delegates are spawned with an explicit model override resolved from
|
|
31
|
+
`.datum/config.json` via `npx @kontourai/datum resolve <role> --json` — see
|
|
32
|
+
`context/contracts/execution-contract.md` § Delegation: Model Routing:
|
|
33
|
+
|
|
34
|
+
| Delegate | Role |
|
|
35
|
+
|---|---|
|
|
36
|
+
| tool-worker | `delegate-mechanical` for fully-specified mechanical slices, `delegate-implementation` for precisely-planned implementation (RED/GREEN/REFACTOR), `delegate-design` when a slice needs design latitude |
|
|
37
|
+
| tool-planner | `delegate-design` |
|
|
38
|
+
| tool-code-reviewer / tool-security-reviewer | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
39
|
+
| tool-verifier / tool-playwright | `delegate-implementation` by default, raised to the worker's tier when higher — never below the tier of the checked work (Goodhart guard) |
|
|
40
|
+
|
|
41
|
+
On a review/verify gate failure, re-dispatch the fix one tier higher and record
|
|
42
|
+
the escalation (contract § Escalation on gate failure). Fallback: inherit the
|
|
43
|
+
session model when datum/config is absent, noted in the artifact.
|
|
44
|
+
|
|
28
45
|
## Orchestrator Rule
|
|
29
46
|
|
|
30
47
|
Same as deliver: you never touch source files. You coordinate the primitives with TDD-specific context.
|
|
@@ -16,6 +16,17 @@ Verification is not critique. Run `review-work` first when the task needs mainta
|
|
|
16
16
|
| tool-verifier | Code verification, acceptance criteria checking, structured verdicts |
|
|
17
17
|
| tool-playwright | Visual verification, screenshots, accessibility checks |
|
|
18
18
|
|
|
19
|
+
## Model Routing
|
|
20
|
+
|
|
21
|
+
Verify roles never resolve **below** the tier of the work they check (Goodhart
|
|
22
|
+
guard): default `delegate-implementation`, raised to match or exceed the tier
|
|
23
|
+
that produced the work under verification — a verifier of `delegate-design` work
|
|
24
|
+
resolves `delegate-design` or `orchestrator`, never a cheaper tier. Resolve the
|
|
25
|
+
role from `.datum/config.json` (`npx @kontourai/datum resolve <role> --json`) and
|
|
26
|
+
pass the model explicitly. See `context/contracts/execution-contract.md`
|
|
27
|
+
§ Delegation: Model Routing and § Goodhart guard. Fallback: inherit the session
|
|
28
|
+
model when datum/config is absent, noted in the artifact.
|
|
29
|
+
|
|
19
30
|
## Orchestrator Rule
|
|
20
31
|
|
|
21
32
|
You do not review source files. You delegate to tool-verifier and tool-playwright, then read the verdict artifact.
|