Skip to content

feat: layered composable behaviours + opt-in skill-sync (port of #27 onto main)#45

Open
colombod wants to merge 4 commits into
mainfrom
feat/layered-behaviours-opt-in-skill-sync
Open

feat: layered composable behaviours + opt-in skill-sync (port of #27 onto main)#45
colombod wants to merge 4 commits into
mainfrom
feat/layered-behaviours-opt-in-skill-sync

Conversation

@colombod

Copy link
Copy Markdown
Collaborator

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 one tool-context-intelligence-query; multi-server destinations fan-out was added). Supersedes #27.

Three changes:

  1. Layered, composable behaviours. The single monolithic behaviour becomes context-intelligence-navigation-analysis-design, plus an orthogonal context-intelligence-logging (telemetry hook only) and the umbrella context-intelligence. Integrators compose only the layer they need.
  2. Telemetry hook freed from skill loading. hook-context-intelligence no longer fetches the graph-query skill on session start — it is now pure telemetry (event capture + fan-out via destinations). skill_fetcher.py and legacy_content/ removed from the hook; deps unchanged (pathspec/httpx are still required for multi-server fan-out).
  3. Skill-sync relocated to the query tool, opt-in. Skill acquisition now lives on tool-context-intelligence-query (composed by the graph-analyst agent), gated by a new skill_sync_enabled knob — default false (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

  • Tests: 176 passed / 2 skipped (ported spike suite + 45 adversarial edge tests).
  • DTU-proven on main (real packet counts against a capture server, driving the actual shipped module): disabled path (the new default) = ZERO /version and /skills requests; vendored-body swap verified (SHA d03a3f20…); enabled path fires exactly ['/version','/skills/…']; server-down degrades gracefully. 18/18 assertions.
  • Full-mode bundle validation: PASS — 10/10 bundles clean; all hygiene/structure/placement/freshness gates green. Runner included: scripts/validate-full.sh. (One mechanical unadvertised_but_referenced flag on the internal context-intelligence mode is a documented name-collision false positive — see AGENTS.md.)

Notes

colombod and others added 3 commits June 26, 2026 11:30
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>
@colombod

Copy link
Copy Markdown
Collaborator Author

Update — fixed the 12 failing bundle/behaviour-YAML tests + re-validated

Root cause: the behaviour split moved the hook wiring out of the context-intelligence.yaml umbrella (now bundle: + includes: only) into context-intelligence-logging.yaml, but three test-fixture helpers still read the umbrella → found hooks: [] → 12 assertions failed. Pre-existing on this branch; not auth, not the Python code.

Fix (composition-aware, not a hardcode): a shared fail-loud helper resolves the umbrella's local includes: chain (the same composition the runtime performs), unions the layers' hooks/tools, and locates hook-context-intelligence by module name — asserting exactly-one, failing loud on zero/multiple. Added test_umbrella_composes_ci_hook, a regression test that goes red if the logging include is ever removed from the umbrella even when the file still exists on disk.

  • tests/test_bundle.py + tests/test_module_loading.py: 12 failed → 32 passed
  • full hook-module suite: 326 passed, 0 failed

Validation:

  • Full-mode validate-bundle-repo: PASSvalidation_mode: full, 10/10 bundles load via BundleRegistry, behaviors thin, context-sink enforced, packaging clean. (The single mechanical unadvertised_but_referenced flag is the documented context-intelligence name-collision false positive — AGENTS.md is authoritative; the validator itself confirms PASS. Runner: scripts/validate-full.sh.)
  • DTU (end-to-end, real session): the context-intelligence umbrella mounts and fires the telemetry hook (writes events.jsonl + metadata.json; a foundation-only session writes nothing — negative control); composing only context-intelligence-logging fires the hook with zero skill machinery (isolated-cache inventory); the navigation ⊂ analysis ⊂ design layering resolves through the real registry.

Commit 5e97e1b. PR is green.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant