feat(linkedin-studio): de-niche trend-spotter — pillar-driven, no hardcoded beat (B-S1) [skip-docs]

The trend-spotter agent stated its own contract — "the niche lives in the
source list and the user's pillars, never in this agent" — yet contradicted it
by hardcoding the Microsoft/public-sector beat in four surfaces. Resolve the
contradiction so the file is genuinely domain-general (plugin-is-domain-general):
the domain comes from the user's profile/pillars at runtime, never baked in.

- Description: "trending topics in AI, Microsoft, and public sector" ->
  "across the user's content pillars and domain"; trigger phrases "what's
  happening in AI" -> "in my field"/"in my space".
- Mission: drop "intersection of AI, Microsoft technology, and public sector
  digitalization" -> "within the creator's own domain, defined entirely by
  their content pillars and expertise areas, never by a beat baked into this
  agent".
- Content Trigger Classification: "Microsoft platform changes" -> "platform
  changes in the user's stack"; "public sector milestones" -> "sector
  milestones in the user's domain".
- 4-Question Relevance Filter: "Public sector leaders or enterprise AI
  implementers" -> "the user's target audience (per their profile)".
- Anti-pattern example: "AI is changing everything" -> "[topic] is changing
  everything".

- CI: new test-runner Section 17 (trend-spotter de-niche guard) forbids the
  KTG-beat proper nouns (Microsoft|Azure|Copilot|public sector|offentlig
  sektor, case-insensitive) from returning to agents/trend-spotter.md, with a
  non-vacuity self-test mirroring Sections 8/13. Scoped to this one agent
  (B-S1); the wider sweep (B-S2) owns the other surfaces + the
  ai-content-framework.md reference filename. Assertion-count renumbered to
  Section 18. Gate 85 -> 87/0/0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RBMKqPSVbvSZHtQ4heM1UY
This commit is contained in:
Kjell Tore Guttormsen 2026-06-22 20:32:05 +02:00
commit 05a22d2cc7
2 changed files with 73 additions and 12 deletions

View file

@ -28,8 +28,11 @@
# the trends-store binding guard (research-engine slice 2b: the trend store's suite
# stays green and its case count never erodes, now that trend-spotter persists its
# findings through it — KTG-only, skipped for an adopter shipping no deps) in Section
# 16; the assertion-count anti-erosion floor (SC6) in Section 17. All are live below
# (Sections 817).
# 16; the trend-spotter de-niche guard (B-S1: agents/trend-spotter.md names no
# hardcoded vendor/sector beat — the domain comes from the user's pillars at runtime,
# never baked into the agent — with a non-vacuity self-test) in Section 17; the
# assertion-count anti-erosion floor (SC6) in Section 18. All are live below
# (Sections 818).
#
# Usage: bash scripts/test-runner.sh
# bash 3.2-safe: plain arrays only, no `declare -A`, no `mapfile`/`readarray`.
@ -692,7 +695,65 @@ fi
echo ""
# --- Section 17: Assertion-Count Anti-Erosion (SC6) ---
# --- Section 17: Trend-Spotter De-Niche Guard (B-S1) ---
echo "--- Trend-Spotter De-Niche ---"
# The trend-spotter agent states its own contract — "the niche lives in the source
# list and the user's pillars, never in this agent" (Source Scanning Framework). For
# that to be true the agent file must name NO specific vendor or sector beat: the
# domain comes from the user's profile/pillars at runtime, never hardcoded here
# (plugin-is-domain-general). Pre-B-S1 the file contradicted itself, hardcoding the
# Microsoft/public-sector beat in its description, mission, trigger table and
# relevance filter. This guard forbids those KTG-beat proper nouns from returning to
# agents/trend-spotter.md. Scoped to this one agent by design (B-S1); the wider
# de-niche sweep (B-S2) covers the other surfaces. Non-vacuity self-test mirrors
# Sections 8/13: the criterion must catch the beat tokens and ignore generic prose
# (incl. the ai-content-framework.md reference filename, which B-S2 owns).
# Case-insensitive: "Public sector" and "public sector" name the same beat, and a
# future reintroduction could use either case — the positive set locks that in.
NICHE_TOKENS='Microsoft|Azure|Copilot|public sector|offentlig sektor'
TS_SELFTEST_OK=1
while IFS= read -r probe; do
[ -z "$probe" ] && continue
if ! echo "$probe" | grep -qiE "$NICHE_TOKENS"; then
TS_SELFTEST_OK=0; echo " non-vacuity FAIL: beat token not caught -> $probe"
fi
done <<'POSITIVE17'
Microsoft platform changes
Azure updates
Copilot rollout
Public sector leaders
trender i offentlig sektor
POSITIVE17
while IFS= read -r probe; do
[ -z "$probe" ] && continue
if echo "$probe" | grep -qiE "$NICHE_TOKENS"; then
TS_SELFTEST_OK=0; echo " false-positive FAIL: generic prose caught -> $probe"
fi
done <<'NEGATIVE17'
platform changes in the user's stack
sector milestones in the user's domain
the user's content pillars and expertise areas
references/ai-content-framework.md
major product/model releases
NEGATIVE17
if [ "$TS_SELFTEST_OK" -eq 1 ]; then
pass "trend-spotter de-niche self-test: 5 beat tokens caught (case-insensitive), 5 generic forms ignored"
else
fail "trend-spotter de-niche self-test failed — the guard no longer enforces the no-beat criterion"
fi
TS_NICHE_HITS=$(grep -niE "$NICHE_TOKENS" agents/trend-spotter.md 2>/dev/null || true)
if [ -z "$TS_NICHE_HITS" ]; then
pass "trend-spotter names no hardcoded vendor/sector beat (domain comes from pillars)"
else
fail "trend-spotter hardcodes a vendor/sector beat — generalize to pillar-driven prose:"
echo "$TS_NICHE_HITS"
fi
echo ""
# --- Section 18: Assertion-Count Anti-Erosion (SC6) ---
# The lint self-modifies its own checks, so a green run could mask a silently dropped
# assertion. Pin the pre-M0 total (74 pass()+fail() invocations) as a floor; the count
# may only grow (brief-reviewer assumption 3). Runs last so TOTAL_CHECKS sees every prior check.