@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.
Files changed (52) hide show
  1. package/.github/workflows/ci.yml +4 -0
  2. package/CHANGELOG.md +17 -0
  3. package/build/src/cli/assignment-provider.d.ts +45 -0
  4. package/build/src/cli/assignment-provider.js +97 -12
  5. package/build/src/cli/workflow-sidecar.d.ts +14 -4
  6. package/build/src/cli/workflow-sidecar.js +100 -10
  7. package/context/contracts/assignment-provider-contract.md +1 -1
  8. package/context/contracts/execution-contract.md +78 -0
  9. package/context/scripts/hooks/config-protection.js +11 -4
  10. package/context/scripts/hooks/stop-goal-fit.js +259 -4
  11. package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +111 -0
  12. package/evals/ci/run-baseline.sh +2 -0
  13. package/evals/integration/test_checkpoint_signing.sh +4 -3
  14. package/evals/integration/test_gate_lockdown.sh +36 -0
  15. package/evals/integration/test_model_routing_escalation.sh +145 -0
  16. package/evals/integration/test_publish_delivery.sh +14 -6
  17. package/evals/integration/test_stop_hook_release.sh +552 -0
  18. package/evals/integration/test_trust_reconcile_negatives.sh +170 -0
  19. package/evals/run.sh +6 -0
  20. package/evals/static/test_model_routing_hints.sh +107 -0
  21. package/kits/builder/skills/builder-shape/SKILL.md +10 -0
  22. package/kits/builder/skills/deliver/SKILL.md +52 -11
  23. package/kits/builder/skills/design-probe/SKILL.md +10 -0
  24. package/kits/builder/skills/execute-plan/SKILL.md +13 -0
  25. package/kits/builder/skills/fix-bug/SKILL.md +17 -0
  26. package/kits/builder/skills/idea-to-backlog/SKILL.md +10 -0
  27. package/kits/builder/skills/plan-work/SKILL.md +9 -0
  28. package/kits/builder/skills/pull-work/SKILL.md +10 -0
  29. package/kits/builder/skills/review-work/SKILL.md +11 -0
  30. package/kits/builder/skills/tdd-workflow/SKILL.md +17 -0
  31. package/kits/builder/skills/verify-work/SKILL.md +11 -0
  32. package/kits/knowledge/adapters/default-store/index.js +56 -15
  33. package/kits/knowledge/adapters/flow-runner/index.js +912 -16
  34. package/kits/knowledge/adapters/obsidian-store/index.js +29 -11
  35. package/kits/knowledge/adapters/shared/codec.js +124 -0
  36. package/kits/knowledge/docs/store-contract.md +405 -3
  37. package/kits/knowledge/evals/audit-freshness/suite.test.js +92 -1
  38. package/kits/knowledge/evals/consolidate-incremental/suite.test.js +494 -0
  39. package/kits/knowledge/evals/consolidation/suite.test.js +1 -1
  40. package/kits/knowledge/evals/contract-suite/suite.test.js +36 -0
  41. package/kits/knowledge/evals/freshness/suite.test.js +339 -0
  42. package/kits/knowledge/evals/inbound-references/suite.test.js +351 -0
  43. package/kits/knowledge/evals/retirement/suite.test.js +1 -1
  44. package/kits/knowledge/evals/supersede-propagation/suite.test.js +384 -0
  45. package/package.json +1 -1
  46. package/schemas/workflow-handoff.schema.json +6 -0
  47. package/scripts/ci/mint-attestation.js +33 -6
  48. package/scripts/ci/trust-reconcile.js +144 -26
  49. package/scripts/hooks/config-protection.js +11 -4
  50. package/scripts/hooks/stop-goal-fit.js +259 -4
  51. package/src/cli/assignment-provider.ts +110 -12
  52. 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
- If datum or the config is absent, fall back to the runtime's inherited model
39
- and note the fallback in the session artifact.
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 to `delivery/` 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:
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
- Then force-stage the trust artifacts for the delivery commit. They are gitignored
246
- by default (they are runtime artifacts written on every local delivery) — `-f`
247
- commits them deliberately into THIS delivery PR so CI's trust-reconcile job can
248
- reconcile the session's claims against fresh CI results:
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/trust.bundle delivery/trust.checkpoint.json
270
+ git add -f delivery/<slug>/
252
271
  ```
253
272
 
254
- 3. Commit the verified diff, including the force-added `delivery/trust.bundle` and `delivery/trust.checkpoint.json`.
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.