From 9f9c4bbd86ba3614b9fedc19c790cb1e9cc00804 Mon Sep 17 00:00:00 2001 From: Kjell Tore Guttormsen Date: Tue, 23 Jun 2026 12:02:01 +0200 Subject: [PATCH] =?UTF-8?q?feat(linkedin-studio):=20extend=20=C2=A717=20de?= =?UTF-8?q?-niche=20guard=20to=20content-planner=20+=20content-framework?= =?UTF-8?q?=20(B-S2=20follow-up)=20[skip-docs]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The de-niche sweep (B-S1/B-S2) stripped the hardcoded KTG beat (Microsoft|Azure|Copilot|public sector|offentlig sektor) from three surfaces, but §17 only locked agents/trend-spotter.md against its return. Generalize the guard to the full de-niched set so a beat token cannot creep back into the other two: - agents/trend-spotter.md (B-S1) - agents/content-planner.md (B-S2a) - references/content-framework.md (B-S2a) Single shared NICHE_TOKENS criterion + non-vacuity/false-positive self-test (unchanged shape), then a per-file grep loop over the explicit allowlist. Scoped per file BY DESIGN, not tree-wide: references/content-angles.md keeps "Public Sector" as 1 of 6 example industry tables (a KEEP surface — generic illustration, not a beat) and is deliberately NOT guarded; "AI" is the plugin's own subject, not a niche token. Added an "AI-driven content planning ..." negative probe to lock that in (self-test now 5 caught / 6 ignored). Non-vacuity proven by injection: a beat token appended to each new file makes the guard fail naming the file+line; reverts clean. Gate 87→89/0/0 (+2 file checks; assertion-count floor §18 still satisfied). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01RigJBiRFNtFZKCz21qNbQ4 --- scripts/test-runner.sh | 58 ++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/scripts/test-runner.sh b/scripts/test-runner.sh index 4651a0d..c8727e7 100755 --- a/scripts/test-runner.sh +++ b/scripts/test-runner.sh @@ -695,23 +695,28 @@ fi echo "" -# --- Section 17: Trend-Spotter De-Niche Guard (B-S1) --- -echo "--- Trend-Spotter De-Niche ---" +# --- Section 17: De-Niche Guard (B-S1 + B-S2) --- +echo "--- De-Niche Guard ---" -# 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 content-framework.md reference filename — B-S2a recast + renamed it -# from ai-content-framework.md, de-nicheing the file the agent reads). -# 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. +# The de-niche sweep removed the hardcoded KTG beat (Microsoft / public-sector) from +# three surfaces so the domain comes from the user's profile/pillars at runtime, never +# baked in (plugin-is-domain-general): +# - agents/trend-spotter.md (B-S1: pillar-driven scanning; the agent's own +# contract says "the niche lives in the source +# list and the user's pillars, never in this agent") +# - agents/content-planner.md (B-S2a: planner calendar recast off the beat) +# - references/content-framework.md (B-S2a: recast + renamed from ai-content-framework.md) +# Each must name NO specific vendor or sector beat; this guard forbids those KTG-beat +# proper nouns from returning to any of them. Scoped per file BY DESIGN: the check runs +# against this explicit allowlist, NOT the whole tree, because references/content-angles.md +# legitimately lists "Public Sector" as one of six example industry tables (a KEEP surface, +# generic illustration — not a hardcoded beat), and "AI" is the plugin's own subject, not a +# niche token. Non-vacuity self-test mirrors Sections 8/13: the criterion must catch the beat +# tokens and ignore generic prose (incl. the content-framework.md reference filename — B-S2a +# recast + renamed it from ai-content-framework.md — and "AI"-flavoured planner prose, locking +# in that "AI" is never treated as a beat). 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 @@ -737,20 +742,23 @@ sector milestones in the user's domain the user's content pillars and expertise areas references/content-framework.md major product/model releases +AI-driven content planning across the user's pillars 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" + pass "de-niche self-test: 5 beat tokens caught (case-insensitive), 6 generic forms ignored (incl. 'AI' kept as the plugin's own subject)" else - fail "trend-spotter de-niche self-test failed — the guard no longer enforces the no-beat criterion" + fail "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 +for guarded in agents/trend-spotter.md agents/content-planner.md references/content-framework.md; do + NICHE_HITS=$(grep -niE "$NICHE_TOKENS" "$guarded" 2>/dev/null || true) + if [ -z "$NICHE_HITS" ]; then + pass "$guarded names no hardcoded vendor/sector beat (domain comes from pillars)" + else + fail "$guarded hardcodes a vendor/sector beat — generalize to pillar-driven prose:" + echo "$NICHE_HITS" + fi +done echo ""