@kontourai/flow-agents 3.0.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 +12 -0
- package/CHANGELOG.md +32 -0
- package/CONTEXT.md +67 -1
- package/README.md +4 -0
- package/build/src/cli/assignment-provider.d.ts +188 -0
- package/build/src/cli/assignment-provider.js +193 -51
- package/build/src/cli/workflow-sidecar.d.ts +16 -6
- package/build/src/cli/workflow-sidecar.js +492 -45
- package/build/src/lib/flow-resolver.d.ts +12 -6
- package/build/src/lib/flow-resolver.js +30 -14
- package/build/src/tools/validate-source-tree.js +2 -1
- package/context/contracts/assignment-provider-contract.md +10 -1
- package/context/contracts/execution-contract.md +78 -0
- package/context/contracts/probe-docs-write-contract.md +187 -0
- package/context/scripts/hooks/config-protection.js +24 -4
- package/context/scripts/hooks/lib/config-protection-remedies.js +9 -0
- package/context/scripts/hooks/stop-goal-fit.js +262 -5
- package/context/scripts/hooks/workflow-steering.js +42 -0
- package/docs/adr/0001-flow-agents-consumes-flow.md +2 -0
- package/docs/adr/0002-flow-kits-as-extension-unit.md +2 -0
- package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +2 -0
- package/docs/adr/0004-gates-expect-surface-claims.md +2 -0
- package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +2 -0
- package/docs/adr/0006-typescript-first-source-policy.md +2 -0
- package/docs/adr/0007-flow-skill-kit-tool-boundary.md +2 -0
- package/docs/adr/0007-skill-audit.md +2 -0
- package/docs/adr/0008-kit-operation-boundary.md +2 -0
- package/docs/adr/0009-canonical-hook-core-kit-boundary.md +2 -0
- package/docs/adr/0010-workflow-trust-state-as-hachure-bundle.md +2 -0
- package/docs/adr/0011-mcp-posture.md +2 -0
- package/docs/adr/0012-agent-coordination-as-liveness-claims.md +2 -0
- package/docs/adr/0013-context-lifecycle.md +2 -0
- package/docs/adr/0014-core-vs-domain-kit-boundary.md +2 -0
- package/docs/adr/0015-flow-flow-agents-boundary-reconciliation.md +2 -0
- package/docs/adr/0016-three-hard-boundary-model.md +2 -0
- package/docs/adr/0017-anti-gaming-trust-security-model.md +2 -0
- package/docs/adr/0018-freeze-local-shell-heuristics.md +2 -0
- package/docs/adr/0019-kit-dependency-ownership.md +2 -0
- package/docs/adr/0020-trust-reconcile-manifest-and-claim-classification.md +2 -0
- package/docs/adr/0021-assignment-leases-and-stale-claim-takeover.md +3 -1
- package/docs/adr/0022-fail-closed-delivery-reconciliation-with-governed-exemptions.md +113 -0
- package/docs/adr/README.md +49 -0
- package/docs/adr/index.md +34 -0
- package/docs/decisions/agent-coordination.md +20 -0
- package/docs/decisions/anti-gaming-trust-security.md +20 -0
- package/docs/decisions/context-lifecycle.md +18 -0
- package/docs/decisions/core-domain-kit-boundary.md +18 -0
- package/docs/decisions/flow-flow-agents-boundary.md +18 -0
- package/docs/decisions/flow-kit.md +20 -0
- package/docs/decisions/flow-skill-kit-tool-boundary.md +20 -0
- package/docs/decisions/graph-knowledge-provider.md +63 -0
- package/docs/decisions/hook-core-kit-boundary.md +18 -0
- package/docs/decisions/index.md +19 -0
- package/docs/decisions/kit-dependency-ownership.md +18 -0
- package/docs/decisions/kit-operation-boundary.md +18 -0
- package/docs/decisions/knowledge-promote-sub-flow.md +59 -0
- package/docs/decisions/kontour-resource-contract.md +18 -0
- package/docs/decisions/mcp-posture.md +18 -0
- package/docs/decisions/three-hard-boundary-model.md +18 -0
- package/docs/decisions/trust-reconcile.md +20 -0
- package/docs/decisions/typescript-source-policy.md +48 -0
- package/docs/decisions/workflow-enforcement.md +18 -0
- package/docs/decisions/workflow-trust-state.md +20 -0
- package/docs/fixture-ownership.md +1 -1
- package/docs/workflow-usage-guide.md +1 -1
- package/evals/ci/run-baseline.sh +6 -0
- package/evals/fixtures/assignment-provider/hostile-effective-state.json +11 -0
- package/evals/integration/test_checkpoint_signing.sh +4 -3
- package/evals/integration/test_current_json_per_actor.sh +516 -0
- package/evals/integration/test_ensure_session_ownership_guard.sh +435 -0
- package/evals/integration/test_gate_lockdown.sh +46 -0
- package/evals/integration/test_model_routing_escalation.sh +145 -0
- package/evals/integration/test_publish_delivery.sh +14 -6
- package/evals/integration/test_pull_work_liveness_preflight.sh +6 -1
- package/evals/integration/test_stop_hook_release.sh +552 -0
- package/evals/integration/test_trust_reconcile_negatives.sh +170 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +12 -3
- package/evals/run.sh +10 -0
- package/evals/static/test_knowledge_providers.sh +13 -4
- package/evals/static/test_model_routing_hints.sh +107 -0
- package/evals/static/test_workflow_skills.sh +15 -2
- package/kits/builder/skills/builder-shape/SKILL.md +10 -0
- package/kits/builder/skills/deliver/SKILL.md +69 -11
- package/kits/builder/skills/design-probe/SKILL.md +47 -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/pickup-probe/SKILL.md +4 -4
- 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 +147 -18
- package/kits/knowledge/adapters/flow-runner/index.js +912 -16
- package/kits/knowledge/adapters/obsidian-store/index.js +97 -14
- package/kits/knowledge/adapters/shared/codec.js +265 -0
- package/kits/knowledge/docs/README.md +121 -2
- package/kits/knowledge/docs/store-contract.md +517 -7
- 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 +212 -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/kits/knowledge/flows/promote.flow.json +84 -0
- package/kits/knowledge/kit.json +15 -0
- package/kits/knowledge/promote/distill.js +96 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/CONTEXT.md +7 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction-policy.md +12 -0
- package/kits/knowledge/promote/fixtures/contradiction-registry/docs/decisions/cache-eviction.md +12 -0
- package/kits/knowledge/promote/fixtures/repo/CONTEXT.md +11 -0
- package/kits/knowledge/promote/fixtures/repo/docs/decisions/index.md +9 -0
- package/kits/knowledge/promote/fixtures/repo/docs/decisions/liveness-claim.md +12 -0
- package/kits/knowledge/promote/fixtures/session/acceptance.json +9 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-planner/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-verifier/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/agents/tool-worker-1-1/events.jsonl +1 -0
- package/kits/knowledge/promote/fixtures/session/kontourai-flow-agents-287--deliver.md +47 -0
- package/kits/knowledge/promote/fixtures/session/learning.json +35 -0
- package/kits/knowledge/promote/fixtures/session/state.json +19 -0
- package/kits/knowledge/promote/health.js +137 -0
- package/kits/knowledge/promote/index.js +176 -0
- package/kits/knowledge/promote/ingest.js +103 -0
- package/kits/knowledge/promote/lib.js +132 -0
- package/kits/knowledge/promote/link.js +84 -0
- package/kits/knowledge/promote/promote.test.js +174 -0
- package/kits/knowledge/providers/conformance/suite.test.js +18 -0
- package/kits/knowledge/providers/index.js +1 -0
- package/kits/knowledge/providers/neo4j/connection.js +121 -0
- package/kits/knowledge/providers/neo4j/cypher.js +190 -0
- package/kits/knowledge/providers/neo4j/fake-driver.js +123 -0
- package/kits/knowledge/providers/neo4j/fixtures/spike-ground-truth.json +29 -0
- package/kits/knowledge/providers/neo4j/graph-queries.js +210 -0
- package/kits/knowledge/providers/neo4j/index.js +280 -0
- package/kits/knowledge/providers/neo4j/integration.test.js +122 -0
- package/kits/knowledge/providers/neo4j/neo4j.test.js +219 -0
- package/kits/knowledge/providers/neo4j/sync.js +235 -0
- package/package.json +4 -2
- package/schemas/workflow-handoff.schema.json +6 -0
- package/scripts/README.md +1 -0
- package/scripts/ci/mint-attestation.js +33 -6
- package/scripts/ci/trust-reconcile.js +144 -26
- package/scripts/freeze-adrs.mjs +364 -0
- package/scripts/hooks/config-protection.js +24 -4
- package/scripts/hooks/evidence-capture.js +4 -1
- package/scripts/hooks/lib/config-protection-remedies.js +9 -0
- package/scripts/hooks/lib/current-pointer.js +123 -0
- package/scripts/hooks/lib/liveness-heartbeat.js +75 -35
- package/scripts/hooks/stop-goal-fit.js +262 -5
- package/scripts/hooks/workflow-steering.js +42 -0
- package/scripts/statusline/flow-agents-statusline.js +3 -1
- package/src/cli/assignment-provider.ts +239 -59
- package/src/cli/workflow-sidecar.ts +529 -43
- package/src/lib/flow-resolver.ts +35 -14
- package/src/tools/validate-source-tree.ts +2 -1
|
@@ -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."
|
|
@@ -530,19 +530,28 @@ fi
|
|
|
530
530
|
|
|
531
531
|
# AC5 (existing-session continuity): re-running ensure-session against the SAME slug from a
|
|
532
532
|
# DIFFERENT actor never re-derives or overwrites the already-recorded branch (ADR 0021 §5
|
|
533
|
-
# takeover continuity — resume the incumbent's branch, never a parallel one).
|
|
533
|
+
# takeover continuity — resume the incumbent's branch, never a parallel one). #291's
|
|
534
|
+
# ensure-session ownership guard now classifies alpha's still-fresh-assignment/no-liveness
|
|
535
|
+
# claim as `reclaimable` and refuses gamma's entry without an explicit takeover — so this
|
|
536
|
+
# takeover is made explicit via --supersede-stale (ADR 0021 §5's grace-beat/auto-resume
|
|
537
|
+
# protocol is still #294's scope; #291 only wires the explicit, caller-invoked takeover path).
|
|
538
|
+
# The supersede updates the ASSIGNMENT record's actor (assignment/<slug>.json), never
|
|
539
|
+
# state.json — state.json's `branch` field (and the rest of state.json) must stay exactly
|
|
540
|
+
# alpha's, proving the takeover resumes the incumbent's branch rather than reforking one for
|
|
541
|
+
# gamma.
|
|
534
542
|
if flow_agents_node "$WRITER" ensure-session \
|
|
535
543
|
--artifact-root "$SESSION_ROOT" \
|
|
536
544
|
--task-slug branch-derive-a \
|
|
537
545
|
--actor test-actor-gamma \
|
|
546
|
+
--supersede-stale \
|
|
538
547
|
--source-request "A takeover by a different actor must not refork the branch." \
|
|
539
548
|
--summary "Resuming actor gamma should inherit alpha's already-recorded branch." \
|
|
540
549
|
--timestamp "2026-05-09T00:03:12Z" >"$TMPDIR_EVAL/branch-no-rederive.out" 2>"$TMPDIR_EVAL/branch-no-rederive.err" \
|
|
541
550
|
&& rg -q '"branch": "agent/test-actor-alpha/branch-derive-a"' "$BRANCH_DERIVE_A_DIR/state.json" \
|
|
542
551
|
&& ! rg -q 'test-actor-gamma' "$BRANCH_DERIVE_A_DIR/state.json"; then
|
|
543
|
-
_pass "sidecar writer never re-derives an existing session's branch for
|
|
552
|
+
_pass "sidecar writer's explicit --supersede-stale takeover (#291) never re-derives or overwrites an existing session's branch for the new actor (AC5, ADR 0021 §5)"
|
|
544
553
|
else
|
|
545
|
-
_fail "sidecar writer re-derived or overwrote an existing session's branch
|
|
554
|
+
_fail "sidecar writer's explicit --supersede-stale takeover re-derived or overwrote an existing session's branch: $(cat "$TMPDIR_EVAL/branch-no-rederive.out" "$TMPDIR_EVAL/branch-no-rederive.err")"
|
|
546
555
|
fi
|
|
547
556
|
|
|
548
557
|
# ─── #309: init-plan (and advance-state) must never drop an already-recorded branch ────
|
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,8 +203,14 @@ 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 ""
|
|
210
|
+
bash "$EVAL_DIR/integration/test_ensure_session_ownership_guard.sh" || result=1
|
|
211
|
+
echo ""
|
|
212
|
+
bash "$EVAL_DIR/integration/test_current_json_per_actor.sh" || result=1
|
|
213
|
+
echo ""
|
|
206
214
|
bash "$EVAL_DIR/integration/test_liveness_heartbeat.sh" || result=1
|
|
207
215
|
echo ""
|
|
208
216
|
bash "$EVAL_DIR/integration/test_pull_work_liveness_preflight.sh" || result=1
|
|
@@ -278,6 +286,8 @@ run_integration() {
|
|
|
278
286
|
echo ""
|
|
279
287
|
bash "$EVAL_DIR/integration/test_kit_identity_trust.sh" || result=1
|
|
280
288
|
echo ""
|
|
289
|
+
bash "$EVAL_DIR/integration/test_model_routing_escalation.sh" || result=1
|
|
290
|
+
echo ""
|
|
281
291
|
bash "$EVAL_DIR/acceptance/prove-capture-teeth-declared.sh" || result=1
|
|
282
292
|
return $result
|
|
283
293
|
}
|
|
@@ -6,18 +6,27 @@
|
|
|
6
6
|
# - providers/conformance/suite.test.js (AC3: all three providers conform)
|
|
7
7
|
# - providers/health/health-pass.test.js (AC1: vault+git-repo reports;
|
|
8
8
|
# AC2: work-item seeded dupe + broken blocker)
|
|
9
|
+
# - promote/promote.test.js (issue #313 knowledge promote sub-flow:
|
|
10
|
+
# AC1 draft delta+provenance, AC2 contradiction
|
|
11
|
+
# report+merge proposal, AC3 zero external writes)
|
|
12
|
+
# - providers/neo4j/neo4j.test.js (issue #327 neo4j provider, CI-safe: sync
|
|
13
|
+
# idempotency AC1, canonical queries AC3, degradation
|
|
14
|
+
# AC4 — via an injected fake driver, no Docker.
|
|
15
|
+
# Live Neo4j integration.test.js is gated on NEO4J_URI.)
|
|
9
16
|
# Deterministic, dependency-free, fixture-driven — never touches a real board.
|
|
10
17
|
set -uo pipefail
|
|
11
18
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
19
|
cd "$ROOT_DIR"
|
|
13
20
|
|
|
14
|
-
echo "── Knowledge store provider conformance + health (node --test) ──"
|
|
21
|
+
echo "── Knowledge store provider conformance + health + promote sub-flow (node --test) ──"
|
|
15
22
|
|
|
16
23
|
if node --test \
|
|
17
24
|
kits/knowledge/providers/conformance/suite.test.js \
|
|
18
|
-
kits/knowledge/providers/health/health-pass.test.js
|
|
19
|
-
|
|
25
|
+
kits/knowledge/providers/health/health-pass.test.js \
|
|
26
|
+
kits/knowledge/promote/promote.test.js \
|
|
27
|
+
kits/knowledge/providers/neo4j/neo4j.test.js; then
|
|
28
|
+
echo " PASS: knowledge store provider conformance + health verbs + promote sub-flow"
|
|
20
29
|
else
|
|
21
|
-
echo " FAIL: knowledge store provider conformance + health verbs"
|
|
30
|
+
echo " FAIL: knowledge store provider conformance + health verbs + promote sub-flow"
|
|
22
31
|
exit 1
|
|
23
32
|
fi
|
|
@@ -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."
|
|
@@ -101,6 +101,8 @@ BACKLOG_PROVIDER_SETTINGS_SCHEMA="$ROOT/schemas/backlog-provider-settings.schema
|
|
|
101
101
|
VERIFICATION_CONTRACT="$ROOT/context/contracts/verification-contract.md"
|
|
102
102
|
REVIEW_CONTRACT="$ROOT/context/contracts/review-contract.md"
|
|
103
103
|
DELIVERY_CONTRACT="$ROOT/context/contracts/delivery-contract.md"
|
|
104
|
+
DECISION_REGISTRY_CONTRACT="$ROOT/context/contracts/decision-registry-contract.md"
|
|
105
|
+
PROBE_DOCS_WRITE_CONTRACT="$ROOT/context/contracts/probe-docs-write-contract.md"
|
|
104
106
|
EFFECTIVE_BACKLOG_SETTINGS="$ROOT/src/cli/effective-backlog-settings.ts"
|
|
105
107
|
PULL_WORK_PROVIDER="$ROOT/src/cli/pull-work-provider.ts"
|
|
106
108
|
PULL_WORK_PROVIDER_INTEGRATION="$ROOT/evals/integration/test_pull_work_provider.sh"
|
|
@@ -188,6 +190,8 @@ require_file "$PLANNING_CONTRACT" "planning contract"
|
|
|
188
190
|
require_file "$EXECUTION_CONTRACT" "execution contract"
|
|
189
191
|
require_file "$SANDBOX_CONTRACT" "sandbox policy contract"
|
|
190
192
|
require_file "$GOVERNANCE_ADAPTER_CONTRACT" "governance adapter contract"
|
|
193
|
+
require_file "$DECISION_REGISTRY_CONTRACT" "decision registry contract"
|
|
194
|
+
require_file "$PROBE_DOCS_WRITE_CONTRACT" "probe docs-write contract"
|
|
191
195
|
require_file "$WORK_ITEM_CONTRACT" "work item contract"
|
|
192
196
|
require_file "$BACKLOG_PROVIDER_SETTINGS" "backlog provider settings"
|
|
193
197
|
require_file "$BACKLOG_PROVIDER_SETTINGS_SCHEMA" "backlog provider settings schema"
|
|
@@ -754,8 +758,17 @@ require_text "$PICKUP_PROBE" 'planned-base drift summary and drift classificatio
|
|
|
754
758
|
require_text "$PICKUP_PROBE" 'Update `CONTEXT.md` inline only for glossary-style durable terminology decisions' "pickup-probe limits CONTEXT.md updates"
|
|
755
759
|
require_text "$PICKUP_PROBE" 'Challenge glossary conflicts immediately' "pickup-probe challenges domain terminology"
|
|
756
760
|
require_text "$PICKUP_PROBE" 'Create a lazy context file only when a resolved term or workflow concept has no existing home' "pickup-probe creates context lazily"
|
|
757
|
-
require_text "$PICKUP_PROBE" '
|
|
758
|
-
require_text "$PICKUP_PROBE" '
|
|
761
|
+
require_text "$PICKUP_PROBE" 'context/contracts/probe-docs-write-contract.md' "pickup-probe references probe docs-write contract"
|
|
762
|
+
require_text "$PICKUP_PROBE" 'Never propose or create a numbered ADR' "pickup-probe never proposes numbered ADRs"
|
|
763
|
+
require_text "$PICKUP_PROBE" 'Record a decision delta at .docs/decisions/<slug>\.md' "pickup-probe records decision delta"
|
|
764
|
+
require_text "$PICKUP_PROBE" 'propose revise-vs-create' "pickup-probe proposes revise-vs-create against the index"
|
|
765
|
+
require_text "$PICKUP_PROBE" 'session-archive. evidence ref' "pickup-probe links transcript provenance"
|
|
766
|
+
require_text "$DESIGN_PROBE" 'context/contracts/probe-docs-write-contract.md' "design-probe references probe docs-write contract"
|
|
767
|
+
require_text "$DESIGN_PROBE" 'Docs-Write: Two-Delta Emission' "design-probe documents two-delta emission"
|
|
768
|
+
require_text "$DESIGN_PROBE" 'Vocabulary delta' "design-probe documents vocabulary delta"
|
|
769
|
+
require_text "$DESIGN_PROBE" 'Decision delta' "design-probe documents decision delta"
|
|
770
|
+
require_text "$DESIGN_PROBE" 'Transcript provenance' "design-probe documents transcript provenance"
|
|
771
|
+
require_text "$DESIGN_PROBE" 'Do not write a numbered ADR' "design-probe never proposes numbered ADRs"
|
|
759
772
|
require_text "$BUILDER_SHAPE" 'Product-level auto-guidance' "builder-shape guides idea-to-backlog"
|
|
760
773
|
require_text "$BUILDER_SHAPE" 'do not require them to type `idea-to-backlog`' "builder-shape hides primitive typing"
|
|
761
774
|
require_text "$PULL" 'probe_status' "pull-work records machine-checkable probe status"
|
|
@@ -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:
|
|
@@ -277,6 +318,23 @@ After CI passes and the work is merged or otherwise accepted:
|
|
|
277
318
|
2. Archive the working artifacts under `.kontourai/flow-agents/<slug>/archive/` or keep a stable link to them.
|
|
278
319
|
3. Record provider records, verification evidence, durable docs targets, accepted gaps, and follow-up routing in durable docs or provider records.
|
|
279
320
|
4. Promote the relevant plan, decision, evidence, and usage notes into long-lived docs such as `docs/`, `README.md`, or a project decision record.
|
|
321
|
+
|
|
322
|
+
**Assisted promotion path (Knowledge Kit `knowledge.promote` sub-flow).** Rather than
|
|
323
|
+
deciding WHAT to promote by ad-hoc judgement, run the Knowledge Kit promote sub-flow
|
|
324
|
+
(`kits/knowledge/flows/promote.flow.json`, id `knowledge.promote`) over the session
|
|
325
|
+
directory. It ingests the session's plan/evidence/critique/learnings/transcripts,
|
|
326
|
+
distills schema-valid DRAFT decision-registry deltas (per
|
|
327
|
+
`context/contracts/decision-registry-contract.md`), CONTEXT.md vocabulary additions,
|
|
328
|
+
and learning entries, links each to the PR + merge SHA + session archive + touched
|
|
329
|
+
topics, and health-checks the registry for contradictions (proposing merge-repair).
|
|
330
|
+
It is PROPOSALS-ONLY: every output lands under `<session>/proposals/` — it never writes
|
|
331
|
+
docs directly. You apply the drafts you accept, then record what was promoted where with
|
|
332
|
+
the `promote` CLI (below), which stays the recording mechanism. The sub-flow is the
|
|
333
|
+
assisted path; the `promote` claim is the gate. `knowledge.promote` is a true composable
|
|
334
|
+
FlowDefinition (a parent step can `uses_flow: "knowledge.promote"`); it is invoked here
|
|
335
|
+
from the promote step rather than nested as a builder gate because the `promote` CLI —
|
|
336
|
+
not a flow gate — is #312's recording mechanism and the sub-flow's outputs are proposals
|
|
337
|
+
a human/agent applies.
|
|
280
338
|
5. Link the long-lived doc back to the provider record, archived plan artifact, or accepted evidence when useful so future readers can see why and how the feature was built.
|
|
281
339
|
6. Confirm `.kontourai/flow-agents/` runtime artifacts remain untracked before merge to `main`.
|
|
282
340
|
7. **Clean up the workspace once the merge is confirmed.** First verify the merge actually happened from the provider's own record (a merge commit / `mergedAt`) — not a green check or a watcher's exit code. Then honor the `worktree_lifecycle` recorded by `pull-work` (`retain_until: pr_merged`): remove the isolated worktree (`git worktree remove <path>`) and delete the now-merged branch locally and on the remote. Never delete a branch or worktree before the merge is confirmed — a closed-but-unmerged PR or a prematurely deleted branch loses work. The task is not done while it leaves a stale worktree or merged branch behind.
|
|
@@ -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:
|
|
@@ -82,6 +92,43 @@ Maintain a compact running record in the active artifact or conversation when no
|
|
|
82
92
|
|
|
83
93
|
When workflow artifacts exist, update the appropriate session, handoff, Probe record, or planning artifact according to the local artifact contract. Do not invent a project-specific storage format when the repository already defines one.
|
|
84
94
|
|
|
95
|
+
## Docs-Write: Two-Delta Emission
|
|
96
|
+
|
|
97
|
+
Per `context/contracts/probe-docs-write-contract.md`, when a `decisions` entry above is a **decision subject** — a durable, reusable answer to a named domain question, not an implementation detail or transient planning choice — emit two deltas into durable docs in the same motion, before moving to the next branch:
|
|
98
|
+
|
|
99
|
+
1. **Vocabulary delta**: the subject noun must exist as a `CONTEXT.md` glossary term. If absent, coin it first (a tight one- or two-sentence `### <Term>` entry); if present, refine the definition when the crystallized decision changes what it says (e.g. an "open" pointer becoming a stated answer).
|
|
100
|
+
2. **Decision delta**: consult `docs/decisions/index.md` and propose revise-vs-create against the existing topic slugs (this includes any `needs-decision` stub already naming the subject); revise the existing topic file in place, or create `docs/decisions/<slug>.md` per `context/contracts/decision-registry-contract.md`. Run `npm run gen:decisions-index && npm run check:decisions` after writing.
|
|
101
|
+
3. **Transcript provenance**: add a `session-archive` evidence ref pointing at this Probe's own session artifact (the live `.kontourai/flow-agents/<slug>/...` path — it does not need to be archived yet).
|
|
102
|
+
|
|
103
|
+
Example — coining a new term and creating its topic file:
|
|
104
|
+
|
|
105
|
+
```markdown
|
|
106
|
+
### Retry Budget
|
|
107
|
+
|
|
108
|
+
The maximum number of automatic retries a workflow step may attempt before routing back to the user. Configured per Flow Definition step, not globally.
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```markdown
|
|
112
|
+
---
|
|
113
|
+
status: current
|
|
114
|
+
subject: Retry budget
|
|
115
|
+
decided: 2026-07-03
|
|
116
|
+
evidence:
|
|
117
|
+
- kind: session-archive
|
|
118
|
+
ref: .kontourai/flow-agents/<slug>/<slug>--design-probe.md
|
|
119
|
+
- kind: issue
|
|
120
|
+
ref: https://github.com/kontourai/flow-agents/issues/<n>
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Retry budget
|
|
124
|
+
|
|
125
|
+
Each Flow Definition step declares its own retry budget; there is no global default. Set during design-probe alignment on <n>.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Example — revising a `needs-decision` stub seeded by the ADR-freeze cutover (issue #314) once its frozen ADR's decision is confirmed still current: flip `status: needs-decision` to `current`, set `decided` to today, keep the existing `adr` evidence ref, and append the `session-archive` ref for this Probe session plus a `pr` ref once one exists. See `context/contracts/probe-docs-write-contract.md` § Worked Example for the full step-by-step.
|
|
129
|
+
|
|
130
|
+
Do not write a numbered ADR — `docs/adr/` is frozen history (`docs/adr/README.md`); every decision delta targets the topic-keyed registry.
|
|
131
|
+
|
|
85
132
|
## Stop Conditions
|
|
86
133
|
|
|
87
134
|
Stop probing when one of these is true:
|