feat: layered composable behaviours + opt-in skill-sync (port of #27 onto main)#45
feat: layered composable behaviours + opt-in skill-sync (port of #27 onto main)#45colombod wants to merge 4 commits into
Conversation
PORT of the proven spike (PR #27) onto main, re-targeted, DTU-proven. Three changes: (a) Layered, composable behaviours: navigation ⊂ analysis ⊂ design + orthogonal logging + umbrella (replaces the single monolith); skills union preserved, modes path + tool-delegate transitive. (b) Telemetry hook freed from skill loading: removed skill_fetcher.py + legacy_content/ + all skill-fetch sites from mount(); hook is now pure telemetry. Deps unchanged (pathspec/httpx still needed for multi-server fan-out). build_payload coupling to tool-context-intelligence-upload preserved. (c) Skill-sync relocated into tool-context-intelligence-query: skill_fetcher.py + skill_sync.py + SHA-pinned bundled_skill/, wired via on_session_ready + the context_intelligence._graph_query_tool capability; new skill_sync_enabled knob on ToolConfigResolver, DEFAULT=FALSE (opt-in); reuses main's existing _resolve_server_config (sources→destinations→env); fail-loud runtime SHA check on vendored-body install. Tests: 176 passed / 2 skipped (ported spike suite + 45 adversarial edges). DTU-proven on main: disabled path emits ZERO network (18/18 assertions; default FALSE = 0 packets); enabled path fetches as expected. Reference: spike branch feat/spike-readonly-behaviour. Addresses the per-session skill-tax overhead for headless/single-command-series workflows. Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…d skill-sync-flow diagram, update README bundle.dot now shows the 5 layered behaviours instead of the monolith. skill-sync-flow diagram ported from spike and retargeted to tool-context-intelligence-query, reflecting the default-FALSE (zero-network) path and fail-loud SHA verification. README documents the opt-in skill_sync_enabled knob (default FALSE), pure-telemetry hook design, layered behaviour composition, and updated repo structure. Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
…r false positive) validate-full.sh runs the bundle validator in validation_mode: full (proven PASS — 10/10 bundles clean, mode-advertising error confirmed a name-collision false positive); AGENTS.md records the false positive so it isn't re-flagged.
…tests)
The behaviour split turned context-intelligence.yaml into a thin umbrella
(bundle: + includes: only; no hooks:). Three fixture helpers in two test
files still read that file directly and found hooks:[] — causing 12 tests
to fail with 'behavior must wire the hook-context-intelligence hook'.
Root cause: all three helpers hardcoded a read of the umbrella YAML,
bypassing the includes chain. The CI hook now lives in
context-intelligence-logging.yaml, reached transitively via includes:.
Fix — test the COMPOSITION, not a hardcoded filename:
1. tests/helpers.py — two new shared helpers:
- composed_behavior(start='context-intelligence'): starts at the named
umbrella YAML, recursively resolves every local includes: entry
(context-intelligence:behaviors/<stem>) into REPO_ROOT/behaviors/<stem>.yaml,
ignores external git+https:// refs, guards against cycles, and returns a
composed dict {hooks:[union], tools:[union]} across all visited layers.
- ci_hook(composed): locates hook-context-intelligence by module name,
enforces exactly-one semantics — fails loud on zero (chain broken?) AND
on >1 (duplicate declaration).
2. test_bundle.py:
- _load_behavior() now delegates to composed_behavior() (was: raw umbrella read).
- _ci_hook() now delegates to ci_hook() (was: inline filter with a bare assert).
- test_behavior_has_hooks_section: asserts the composed view has a non-empty
hooks list (umbrella itself has no hooks: key).
- test_umbrella_composes_ci_hook (new regression test): proves the CI hook is
reachable FROM the umbrella through the includes chain — goes red if someone
removes the logging-behaviour include even if the file still exists on disk.
3. test_module_loading.py:
- TestBundleYamlEntryPointConsistency._load_behavior_yaml() → composed_behavior().
- TestBehaviorYamlConfigShape._load_behavior_yaml() → composed_behavior().
- TestBehaviorYamlConfigShape._ci_hook() → ci_hook().
- Removed unused yaml import.
Results:
before: 12 failed, 19 passed (test_bundle.py + test_module_loading.py)
after: 32 passed, 0 failed (same two files)
full suite: 326 passed, 0 failed, 0 regressions
Also records the known validator false positive (mode-advertising heuristic
matches 'context-intelligence' as a path/skill name, not a slash-command)
in helpers.py docstring so any future reader doesn't re-chase it.
🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Update — fixed the 12 failing bundle/behaviour-YAML tests + re-validatedRoot cause: the behaviour split moved the hook wiring out of the Fix (composition-aware, not a hardcode): a shared fail-loud helper resolves the umbrella's local
Validation:
Commit |
What
Ports the proven, DTU-tested spike (#27) onto current
main, re-targeted because main's module layout diverged (graph-query + blob-read are now onetool-context-intelligence-query; multi-serverdestinationsfan-out was added). Supersedes #27.Three changes:
context-intelligence-navigation⊂-analysis⊂-design, plus an orthogonalcontext-intelligence-logging(telemetry hook only) and the umbrellacontext-intelligence. Integrators compose only the layer they need.hook-context-intelligenceno longer fetches the graph-query skill on session start — it is now pure telemetry (event capture + fan-out viadestinations).skill_fetcher.pyandlegacy_content/removed from the hook; deps unchanged (pathspec/httpx are still required for multi-server fan-out).tool-context-intelligence-query(composed by the graph-analyst agent), gated by a newskill_sync_enabledknob — defaultfalse(opt-in). When off (or no server): a SHA-pinned vendored skill body shipped in the bundle is used, with zero network; when on: version-gate + conditional fetch. Install fails loud on SHA mismatch.Why
Addresses
microsoft-amplifier/amplifier-support#283: the CI behaviour force-fetched the graph-query skill (GET /version+GET /skills/…) on every session start, so headless / single-command-series / pipeline workflows paid a per-session skill tax even when the skill was never used. Also delivers the broader composition-efficiency win — compose only what you need.Proof
main(real packet counts against a capture server, driving the actual shipped module): disabled path (the new default) = ZERO/versionand/skillsrequests; vendored-body swap verified (SHAd03a3f20…); enabled path fires exactly['/version','/skills/…']; server-down degrades gracefully. 18/18 assertions.scripts/validate-full.sh. (One mechanicalunadvertised_but_referencedflag on the internalcontext-intelligencemode is a documented name-collision false positive — seeAGENTS.md.)Notes
skill_sync_enableddefaultfalsewas a deliberate decision (opt-in) so the headless win is delivered by default, not merely enabled.