@event4u/agent-config 2.7.0 → 2.9.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/.agent-src/personas/cmo.md +122 -0
- package/.agent-src/personas/customer-success-lead.md +126 -0
- package/.agent-src/personas/engineering-manager.md +133 -0
- package/.agent-src/personas/finance-partner.md +129 -0
- package/.agent-src/personas/growth-pm.md +134 -0
- package/.agent-src/personas/people-strategist.md +126 -0
- package/.agent-src/personas/revops.md +125 -0
- package/.agent-src/personas/strategist.md +129 -0
- package/.agent-src/skills/activation-design/SKILL.md +160 -0
- package/.agent-src/skills/build-buy-partner/SKILL.md +145 -0
- package/.agent-src/skills/churn-prevention/SKILL.md +156 -0
- package/.agent-src/skills/comp-banding/SKILL.md +160 -0
- package/.agent-src/skills/competitive-moat-analysis/SKILL.md +152 -0
- package/.agent-src/skills/content-funnel-design/SKILL.md +170 -0
- package/.agent-src/skills/contracts-cognition/SKILL.md +147 -0
- package/.agent-src/skills/data-handling-judgment/SKILL.md +155 -0
- package/.agent-src/skills/deal-qualification-meddic/SKILL.md +165 -0
- package/.agent-src/skills/editorial-calendar/SKILL.md +161 -0
- package/.agent-src/skills/expansion-playbook/SKILL.md +171 -0
- package/.agent-src/skills/forecast-accuracy/SKILL.md +157 -0
- package/.agent-src/skills/forecasting/SKILL.md +164 -0
- package/.agent-src/skills/fundraising-narrative/SKILL.md +189 -0
- package/.agent-src/skills/funnel-analysis/SKILL.md +26 -2
- package/.agent-src/skills/gtm-launch/SKILL.md +165 -0
- package/.agent-src/skills/hiring-loop-design/SKILL.md +167 -0
- package/.agent-src/skills/market-entry-analysis/SKILL.md +144 -0
- package/.agent-src/skills/messaging-architecture/SKILL.md +184 -0
- package/.agent-src/skills/onboarding-design/SKILL.md +158 -0
- package/.agent-src/skills/onboarding-program/SKILL.md +157 -0
- package/.agent-src/skills/one-on-one-cadence/SKILL.md +161 -0
- package/.agent-src/skills/org-design/SKILL.md +158 -0
- package/.agent-src/skills/perf-feedback-craft/SKILL.md +157 -0
- package/.agent-src/skills/pipeline-strategy/SKILL.md +159 -0
- package/.agent-src/skills/positioning-strategy/SKILL.md +177 -0
- package/.agent-src/skills/privacy-review/SKILL.md +160 -0
- package/.agent-src/skills/retention-loops/SKILL.md +161 -0
- package/.agent-src/skills/runway-cognition/SKILL.md +136 -0
- package/.agent-src/skills/scenario-modeling/SKILL.md +139 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/.agent-src/skills/throughput-vs-morale-tradeoff/SKILL.md +165 -0
- package/.agent-src/skills/unit-economics-modeling/SKILL.md +54 -7
- package/.agent-src/skills/vision-articulation/SKILL.md +146 -0
- package/.agent-src/skills/voice-and-tone-design/SKILL.md +163 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.agent-src/templates/scripts/telemetry/settings.py +65 -0
- package/.agent-src/templates/scripts/tier_usage_report.py +183 -0
- package/.claude-plugin/marketplace.json +34 -2
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +135 -153
- package/README.md +3 -3
- package/docs/architecture.md +37 -11
- package/docs/archive/CHANGELOG-pre-2.7.0.md +185 -0
- package/docs/catalog.md +38 -4
- package/docs/contracts/adr-forecast-construction-shape.md +89 -0
- package/docs/contracts/adr-gtm-context-spine.md +115 -0
- package/docs/contracts/adr-wing4-context-spine.md +125 -0
- package/docs/contracts/command-clusters.md +41 -0
- package/docs/contracts/command-surface-tiers.md +30 -9
- package/docs/contracts/context-spine.md +58 -12
- package/docs/contracts/cross-wing-handoff.md +3 -3
- package/docs/contracts/mcp-beta-criteria.md +129 -0
- package/docs/contracts/persona-schema.md +20 -3
- package/docs/guidelines/gtm-handoff.md +114 -0
- package/docs/guidelines/wing4-handoff.md +127 -0
- package/docs/mcp-server.md +1 -1
- package/package.json +1 -1
- package/scripts/_cli/cmd_doctor.py +527 -14
- package/scripts/_cli/cmd_validate.py +10 -0
- package/scripts/agent-config +19 -18
- package/scripts/install.py +5 -0
- package/scripts/lint_context_spine_usage.py +5 -1
- package/scripts/mcp_server/__init__.py +1 -0
- package/scripts/mcp_server/server.py +4 -3
- package/scripts/schemas/persona.schema.json +5 -0
- package/scripts/schemas/skill.schema.json +2 -2
- package/scripts/skill_linter.py +284 -6
package/scripts/agent-config
CHANGED
|
@@ -62,17 +62,6 @@ Tier 0 — daily-driver (init → sync → validate → work):
|
|
|
62
62
|
(Option-A loop; called by the /work command)
|
|
63
63
|
implement-ticket Drive the work_engine Python engine on a ticket envelope
|
|
64
64
|
(Option-A loop; called by the /implement-ticket command)
|
|
65
|
-
first-run Guided first-run setup — cost profile, settings, tooling
|
|
66
|
-
keys:install-anthropic Install the Anthropic API key for the AI Council
|
|
67
|
-
(interactive, /dev/tty only, writes ~/.config/agent-config/anthropic.key 0600)
|
|
68
|
-
keys:install-openai Install the OpenAI API key for the AI Council
|
|
69
|
-
(interactive, /dev/tty only, writes ~/.config/agent-config/openai.key 0600)
|
|
70
|
-
council:estimate Pre-call council cost preview (no API call, no spend)
|
|
71
|
-
Usage: council:estimate <question> [--input-mode prompt|roadmap]
|
|
72
|
-
council:run Run the council. Requires --confirm to spend.
|
|
73
|
-
Usage: council:run <question> --output <path> --confirm
|
|
74
|
-
council:render Re-render a saved council responses JSON to markdown
|
|
75
|
-
Usage: council:render <responses.json>
|
|
76
65
|
help Show this help (default Tier-0; --tier=1|all expands)
|
|
77
66
|
--version, -V Print package version
|
|
78
67
|
EOF
|
|
@@ -110,6 +99,17 @@ Tier 1 — power-user (release shape, audit, migration):
|
|
|
110
99
|
Flags: --json | --project=<path>
|
|
111
100
|
migrate One-shot migration off legacy composer / npm install paths
|
|
112
101
|
Flags: --dry-run (detect only)
|
|
102
|
+
first-run Guided first-run setup — cost profile, settings, tooling
|
|
103
|
+
keys:install-anthropic Install the Anthropic API key for the AI Council
|
|
104
|
+
(interactive, /dev/tty only, writes ~/.config/agent-config/anthropic.key 0600)
|
|
105
|
+
keys:install-openai Install the OpenAI API key for the AI Council
|
|
106
|
+
(interactive, /dev/tty only, writes ~/.config/agent-config/openai.key 0600)
|
|
107
|
+
council:estimate Pre-call council cost preview (no API call, no spend)
|
|
108
|
+
Usage: council:estimate <question> [--input-mode prompt|roadmap]
|
|
109
|
+
council:run Run the council. Requires --confirm to spend.
|
|
110
|
+
Usage: council:run <question> --output <path> --confirm
|
|
111
|
+
council:render Re-render a saved council responses JSON to markdown
|
|
112
|
+
Usage: council:render <responses.json>
|
|
113
113
|
EOF
|
|
114
114
|
fi
|
|
115
115
|
|
|
@@ -124,6 +124,7 @@ Tier 2 — maintenance / internal (hooks, MCP, memory, telemetry):
|
|
|
124
124
|
(one-line MCP server onboarding; idempotent)
|
|
125
125
|
mcp:run Run the built-in MCP server over stdio
|
|
126
126
|
(requires `mcp:setup` first; see docs/mcp-server.md)
|
|
127
|
+
(experimental — beta gates: docs/contracts/mcp-beta-criteria.md)
|
|
127
128
|
roadmap:progress Regenerate agents/roadmaps-progress.md from open roadmaps
|
|
128
129
|
roadmap:progress-check Fail if agents/roadmaps-progress.md is stale (for CI)
|
|
129
130
|
hooks:install Install the pre-commit roadmap-progress hook
|
|
@@ -161,7 +162,7 @@ EOF
|
|
|
161
162
|
if [[ "$tier" == "0" ]]; then
|
|
162
163
|
cat <<'EOF'
|
|
163
164
|
|
|
164
|
-
(Hidden:
|
|
165
|
+
(Hidden: 15 Tier-1 + 26 Tier-2 commands. Run `./agent-config --help --tier=1`
|
|
165
166
|
or `--tier=all` to see them. Tier criteria: docs/contracts/command-surface-tiers.md.)
|
|
166
167
|
EOF
|
|
167
168
|
fi
|
|
@@ -173,14 +174,8 @@ Examples (Tier 0):
|
|
|
173
174
|
./agent-config sync --dry-run
|
|
174
175
|
./agent-config sync
|
|
175
176
|
./agent-config validate
|
|
176
|
-
./agent-config first-run
|
|
177
177
|
./agent-config work --state-file .work-state.json --prompt-file prompt.txt
|
|
178
178
|
./agent-config implement-ticket --state-file .work-state.json
|
|
179
|
-
./agent-config keys:install-anthropic
|
|
180
|
-
./agent-config keys:install-openai
|
|
181
|
-
./agent-config council:estimate prompt.txt
|
|
182
|
-
./agent-config council:run prompt.txt --output agents/council-sessions/out.json --confirm
|
|
183
|
-
./agent-config council:render agents/council-sessions/out.json
|
|
184
179
|
EOF
|
|
185
180
|
|
|
186
181
|
if [[ "$tier" == "1" || "$tier" == "all" ]]; then
|
|
@@ -203,6 +198,12 @@ Examples (Tier 1):
|
|
|
203
198
|
./agent-config versions --json
|
|
204
199
|
./agent-config init --offline --tools=claude-code,cursor --yes
|
|
205
200
|
./agent-config update --offline --to=2.2.0
|
|
201
|
+
./agent-config first-run
|
|
202
|
+
./agent-config keys:install-anthropic
|
|
203
|
+
./agent-config keys:install-openai
|
|
204
|
+
./agent-config council:estimate prompt.txt
|
|
205
|
+
./agent-config council:run prompt.txt --output agents/council-sessions/out.json --confirm
|
|
206
|
+
./agent-config council:render agents/council-sessions/out.json
|
|
206
207
|
EOF
|
|
207
208
|
fi
|
|
208
209
|
|
package/scripts/install.py
CHANGED
|
@@ -105,6 +105,11 @@ def warn(msg: str) -> None:
|
|
|
105
105
|
|
|
106
106
|
def fail(msg: str) -> "None":
|
|
107
107
|
print(f" ❌ {msg}", file=sys.stderr)
|
|
108
|
+
print(
|
|
109
|
+
" Diagnose: `./agent-config doctor` "
|
|
110
|
+
"(or `--check <id>` for a single category)",
|
|
111
|
+
file=sys.stderr,
|
|
112
|
+
)
|
|
108
113
|
sys.exit(1)
|
|
109
114
|
|
|
110
115
|
|
|
@@ -31,7 +31,11 @@ SKILL_GLOBS = (
|
|
|
31
31
|
".agent-src.uncompressed/skills/**/SKILL.md",
|
|
32
32
|
".agent-src/skills/**/SKILL.md",
|
|
33
33
|
)
|
|
34
|
-
VALID_SLOTS = (
|
|
34
|
+
VALID_SLOTS = (
|
|
35
|
+
"product", "team", "repo",
|
|
36
|
+
"channel-stage", "funnel-stage", "customer-segment",
|
|
37
|
+
"fiscal-period", "org-stage", "regulatory-regime",
|
|
38
|
+
)
|
|
35
39
|
|
|
36
40
|
CONTEXT_SPINE_PAT = re.compile(
|
|
37
41
|
r"^context_spine:\s*\[([^\]]*)\]\s*$", re.MULTILINE
|
|
@@ -11,6 +11,7 @@ boundary in `agents/roadmaps/road-to-mcp-server.md`. No `tools`
|
|
|
11
11
|
primitive, no engine spawn, no shell execution.
|
|
12
12
|
|
|
13
13
|
Stability: experimental. Contract: `docs/contracts/mcp-phase-1-scope.md`.
|
|
14
|
+
Promotion to beta gated on `docs/contracts/mcp-beta-criteria.md`.
|
|
14
15
|
"""
|
|
15
16
|
from __future__ import annotations
|
|
16
17
|
|
|
@@ -125,9 +125,10 @@ def build_server(
|
|
|
125
125
|
name=SERVER_NAME,
|
|
126
126
|
version=__version__,
|
|
127
127
|
instructions=(
|
|
128
|
-
"agent-config MCP server (Phase 3, experimental
|
|
129
|
-
"
|
|
130
|
-
"
|
|
128
|
+
"agent-config MCP server (Phase 3, experimental; beta gates "
|
|
129
|
+
"in docs/contracts/mcp-beta-criteria.md). Exposes all skills "
|
|
130
|
+
"+ commands as instructional prompts, plus rules + guidelines "
|
|
131
|
+
"+ contexts as read-only resources."
|
|
131
132
|
),
|
|
132
133
|
)
|
|
133
134
|
|
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
"type": "string",
|
|
27
27
|
"enum": ["core", "specialist"]
|
|
28
28
|
},
|
|
29
|
+
"wing": {
|
|
30
|
+
"type": "integer",
|
|
31
|
+
"enum": [1, 2, 3, 4],
|
|
32
|
+
"description": "Cognition wing per docs/contracts/package-self-orientation.md § The four wings. Optional. Raises the specialist size cap (persona-schema § 4)."
|
|
33
|
+
},
|
|
29
34
|
"mode": {
|
|
30
35
|
"type": "string",
|
|
31
36
|
"enum": ["developer", "reviewer", "tester", "product-owner", "incident", "planner"]
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"uniqueItems": true,
|
|
73
73
|
"items": {
|
|
74
74
|
"type": "string",
|
|
75
|
-
"enum": ["product", "team", "repo"]
|
|
75
|
+
"enum": ["product", "team", "repo", "channel-stage", "funnel-stage", "customer-segment", "fiscal-period", "org-stage", "regulatory-regime"]
|
|
76
76
|
},
|
|
77
|
-
"description": "Senior-skill opt-in for the
|
|
77
|
+
"description": "Senior-skill opt-in for the context spine. Declares which slots under agents/context-spine/ the skill expects to read. Cross-wing slots (product, team, repo) are locked at 3 by council Q1 (KEEP-3); wing-scoped slots follow the per-wing ADR track in docs/contracts/context-spine.md § 5. Wing-3 (channel-stage, funnel-stage, customer-segment) authorized by docs/contracts/adr-gtm-context-spine.md; Wing-4 (fiscal-period, org-stage, regulatory-regime) authorized by docs/contracts/adr-wing4-context-spine.md."
|
|
78
78
|
},
|
|
79
79
|
"execution": {
|
|
80
80
|
"type": "object",
|
package/scripts/skill_linter.py
CHANGED
|
@@ -57,6 +57,15 @@ REQUIRED_PERSONA_SECTIONS = REQUIRED_PERSONA_SECTIONS_CORE
|
|
|
57
57
|
VALID_PERSONA_TIERS = {"core", "specialist"}
|
|
58
58
|
# Locked in docs/contracts/persona-schema.md § 4: core ≤ 120, specialist ≤ 100.
|
|
59
59
|
PERSONA_LINE_BUDGETS = {"core": 120, "specialist": 100}
|
|
60
|
+
# Wing-scoped overrides — Wing-3 (GTM) and Wing-4 (Money/Strategy/Ops) carry
|
|
61
|
+
# denser cognition (funnel × channel × lifecycle, or finance × org × strategy)
|
|
62
|
+
# than Wing-1/2 specialists, so the line cap rises to keep the seven-section
|
|
63
|
+
# spine intact without amputating workflows. Persona-schema.md § 4 wing matrix.
|
|
64
|
+
VALID_PERSONA_WINGS = {1, 2, 3, 4}
|
|
65
|
+
PERSONA_LINE_BUDGETS_BY_WING = {
|
|
66
|
+
("specialist", 3): 140,
|
|
67
|
+
("specialist", 4): 140,
|
|
68
|
+
}
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
REQUIRED_SKILL_SECTIONS = [
|
|
@@ -161,6 +170,89 @@ VALID_EXECUTION_HANDLERS = {"none", "shell", "php", "node", "internal"}
|
|
|
161
170
|
VALID_EXECUTION_SAFETY_MODES = {"strict"}
|
|
162
171
|
VALID_EXECUTION_FIELDS = {"type", "handler", "timeout_seconds", "safety_mode", "allowed_tools", "command"}
|
|
163
172
|
|
|
173
|
+
# --- Wing-3 GTM cognition-boundary patterns (council Q7 / iter-2 OQ3) ---
|
|
174
|
+
# Triggered only when a skill's context_spine declares a Wing-3 slot.
|
|
175
|
+
# See docs/contracts/adr-gtm-context-spine.md and
|
|
176
|
+
# agents/roadmaps/road-to-gtm-and-growth.md § G2.
|
|
177
|
+
WING3_SPINE_SLOTS = {"channel-stage", "funnel-stage", "customer-segment"}
|
|
178
|
+
|
|
179
|
+
CONTEXT_SPINE_INLINE_PATTERN = re.compile(
|
|
180
|
+
r'^context_spine:\s*\[(.*?)\]\s*$', re.MULTILINE
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# agent-operability: external SaaS URLs the agent would have to auth against
|
|
184
|
+
WING3_SAAS_URL_PATTERN = re.compile(
|
|
185
|
+
r"https?://[\w.-]*\.(salesforce|hubspot|marketo|pardot|mailchimp|"
|
|
186
|
+
r"intercom|amplitude|mixpanel|segment|klaviyo|sendgrid|mailgun|"
|
|
187
|
+
r"pendo|gong|outreach|salesloft|apollo)\.(com|io)\b",
|
|
188
|
+
re.IGNORECASE,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# vendor-independence: brand / SDK / platform slugs that lock cognition
|
|
192
|
+
WING3_VENDOR_BLACKLIST = re.compile(
|
|
193
|
+
r"\b(salesforce|hubspot|marketo|pardot|mailchimp|intercom|drift|"
|
|
194
|
+
r"klaviyo|sendgrid|mailgun|amplitude|mixpanel|pendo|gong|"
|
|
195
|
+
r"outreach\.io|salesloft|apollo\.io|zendesk|freshworks)\b",
|
|
196
|
+
re.IGNORECASE,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# transferability: stack-locked tooling instructions
|
|
200
|
+
WING3_STACK_LOCKED_PATTERN = re.compile(
|
|
201
|
+
r"\b(npm install|pip install|composer require|gem install|"
|
|
202
|
+
r"cargo add|yarn add|pnpm add|bundle add)\s+[\w@/.-]+",
|
|
203
|
+
re.IGNORECASE,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# channel-agnosticism: channel-specific tactical prescriptions
|
|
207
|
+
WING3_CHANNEL_TACTIC_PATTERN = re.compile(
|
|
208
|
+
r"\b(email subject line|tweet length|linkedin (post|ad)|"
|
|
209
|
+
r"facebook ad|google ads?|tiktok (post|video)|instagram (post|reel)|"
|
|
210
|
+
r"sms character limit|cold email template)\b",
|
|
211
|
+
re.IGNORECASE,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# --- Wing-4 Money/Strategy/Ops cognition-boundary patterns (council Q7 / J2) ---
|
|
215
|
+
# Triggered only when a skill's context_spine declares a Wing-4 slot.
|
|
216
|
+
# See docs/contracts/adr-wing4-context-spine.md and
|
|
217
|
+
# agents/roadmaps/road-to-money-strategy-ops.md § J2.
|
|
218
|
+
WING4_SPINE_SLOTS = {"fiscal-period", "org-stage", "regulatory-regime"}
|
|
219
|
+
|
|
220
|
+
# agent-operability: external finance / HR / legal SaaS URLs
|
|
221
|
+
WING4_SAAS_URL_PATTERN = re.compile(
|
|
222
|
+
r"https?://[\w.-]*\.(quickbooks|intuit|netsuite|xero|sage|"
|
|
223
|
+
r"carta|pulley|gusto|bamboohr|lattice|15five|justworks|"
|
|
224
|
+
r"docusign|ironclad|onetrust|rippling|workday|deel|"
|
|
225
|
+
r"namely|adp|paychex|trinet|hibob|cultureamp)\.(com|io|co)\b",
|
|
226
|
+
re.IGNORECASE,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# vendor-independence: finance / HR / legal brand / SDK slugs
|
|
230
|
+
WING4_VENDOR_BLACKLIST = re.compile(
|
|
231
|
+
r"\b(quickbooks|netsuite|xero|sage intacct|"
|
|
232
|
+
r"carta|pulley|gusto|bamboohr|lattice|15five|justworks|"
|
|
233
|
+
r"docusign|ironclad|onetrust|rippling|workday|deel|"
|
|
234
|
+
r"namely|adp|paychex|trinet|hibob|culture amp)\b",
|
|
235
|
+
re.IGNORECASE,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# stage-agnosticism: prescriptive stage-specific thresholds that lock cognition
|
|
239
|
+
# Catches hardcoded runway / ARR / burn / team-size prescriptions tied to a
|
|
240
|
+
# specific funding stage. Framework-style framing ("read the org-stage slot",
|
|
241
|
+
# "applies across seed and public") passes; hard prescriptions ("18 months of
|
|
242
|
+
# runway", "Series A teams must hire") fire.
|
|
243
|
+
WING4_STAGE_AGNOSTIC_PATTERN = re.compile(
|
|
244
|
+
r"(?:"
|
|
245
|
+
r"\b\d+\s+months?\s+of\s+runway\b"
|
|
246
|
+
r"|\brunway\s+of\s+at\s+least\s+\d+\s+months?\b"
|
|
247
|
+
r"|\bminimum\s+runway\s+of\s+\d+\b"
|
|
248
|
+
r"|\b(?:seed|series\s+[a-d]|growth|pre-?ipo|post-?ipo)[-\s]stage\s+"
|
|
249
|
+
r"(?:companies|startups|teams|founders|orgs)\s+(?:must|should|always|never)\b"
|
|
250
|
+
r"|\bteam\s+of\s+\d+\s+(?:or\s+more|or\s+fewer)\b"
|
|
251
|
+
r"|\b(?:arr|mrr|burn\s+rate)\s+(?:of|over|under|above|below)\s+\$\d+"
|
|
252
|
+
r")",
|
|
253
|
+
re.IGNORECASE,
|
|
254
|
+
)
|
|
255
|
+
|
|
164
256
|
|
|
165
257
|
@dataclass
|
|
166
258
|
class Issue:
|
|
@@ -569,9 +661,9 @@ def lint_skill(path: Path, text: str) -> LintResult:
|
|
|
569
661
|
skill_name = path.parent.name if path.name == "SKILL.md" else path.stem
|
|
570
662
|
if skill_name and "-" not in skill_name and len(skill_name) >= 3:
|
|
571
663
|
# Single word without qualifier — likely too generic
|
|
572
|
-
ALLOWED_BARE_NOUNS = {"database", "devcontainer", "docker", "eloquent", "flux", "
|
|
573
|
-
"laravel", "livewire", "mcp", "openapi", "performance",
|
|
574
|
-
"terraform", "terragrunt", "traefik", "websocket"}
|
|
664
|
+
ALLOWED_BARE_NOUNS = {"database", "devcontainer", "docker", "eloquent", "flux", "forecasting",
|
|
665
|
+
"grafana", "laravel", "livewire", "mcp", "openapi", "performance",
|
|
666
|
+
"security", "terraform", "terragrunt", "traefik", "websocket"}
|
|
575
667
|
if skill_name.lower() not in ALLOWED_BARE_NOUNS:
|
|
576
668
|
issues.append(Issue("warning", "bare_noun_name",
|
|
577
669
|
f"Bare-noun skill name `{skill_name}` — consider adding a qualifier (e.g., `{skill_name}-management`)"))
|
|
@@ -605,6 +697,15 @@ def lint_skill(path: Path, text: str) -> LintResult:
|
|
|
605
697
|
if tier_match and tier_match.group(1) == "senior":
|
|
606
698
|
issues.extend(lint_senior_tier_blocks(text))
|
|
607
699
|
|
|
700
|
+
# --- Wing-3 GTM cognition-boundary check (council Q7 / adr-gtm-context-spine.md) ---
|
|
701
|
+
spine_slots = parse_context_spine(frontmatter)
|
|
702
|
+
if spine_slots and any(s in WING3_SPINE_SLOTS for s in spine_slots):
|
|
703
|
+
issues.extend(lint_wing3_boundaries(text))
|
|
704
|
+
|
|
705
|
+
# --- Wing-4 Money/Strategy/Ops cognition-boundary check (council Q7 / J2) ---
|
|
706
|
+
if spine_slots and any(s in WING4_SPINE_SLOTS for s in spine_slots):
|
|
707
|
+
issues.extend(lint_wing4_boundaries(text))
|
|
708
|
+
|
|
608
709
|
procedure_block = find_procedure_block(text)
|
|
609
710
|
if procedure_block is not None:
|
|
610
711
|
if not procedure_block:
|
|
@@ -993,6 +1094,156 @@ def lint_senior_tier_blocks(text: str) -> List[Issue]:
|
|
|
993
1094
|
return issues
|
|
994
1095
|
|
|
995
1096
|
|
|
1097
|
+
def parse_context_spine(frontmatter: str) -> Optional[List[str]]:
|
|
1098
|
+
"""Parse `context_spine:` from frontmatter.
|
|
1099
|
+
|
|
1100
|
+
Supports the inline form `context_spine: [a, b, c]` (most skills) and
|
|
1101
|
+
the block form via `_parse_yaml_list`. Returns the slot list, ``[]``
|
|
1102
|
+
for an explicitly empty array, or ``None`` if the key is absent.
|
|
1103
|
+
"""
|
|
1104
|
+
match = CONTEXT_SPINE_INLINE_PATTERN.search(frontmatter)
|
|
1105
|
+
if match is not None:
|
|
1106
|
+
inner = match.group(1).strip()
|
|
1107
|
+
if not inner:
|
|
1108
|
+
return []
|
|
1109
|
+
return [s.strip().strip('"').strip("'") for s in inner.split(",") if s.strip()]
|
|
1110
|
+
block = _parse_yaml_list(frontmatter, "context_spine")
|
|
1111
|
+
return block
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
def _strip_wing3_carve_outs(text: str) -> str:
|
|
1115
|
+
"""Remove fenced code, inline backticks, the ``## Do NOT`` block, and
|
|
1116
|
+
``**WHEN NOT to use this**`` bullets so legitimate citations of vendor
|
|
1117
|
+
names (as off-scope examples) do not trip Wing-3 boundary checks.
|
|
1118
|
+
"""
|
|
1119
|
+
text = re.sub(r"```[^\n]*\n.*?```", "", text, flags=re.DOTALL)
|
|
1120
|
+
text = re.sub(r"`[^`]+`", "", text)
|
|
1121
|
+
text = re.sub(
|
|
1122
|
+
r"^##\s+Do NOT\s*$.*?(?=^##\s+|\Z)",
|
|
1123
|
+
"", text, flags=re.MULTILINE | re.DOTALL,
|
|
1124
|
+
)
|
|
1125
|
+
text = re.sub(
|
|
1126
|
+
r"\*\*WHEN NOT to use this\*\*.*?(?=\*\*WHEN|^##\s+|\Z)",
|
|
1127
|
+
"", text, flags=re.DOTALL | re.IGNORECASE,
|
|
1128
|
+
)
|
|
1129
|
+
return text
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
def lint_wing3_boundaries(text: str) -> List[Issue]:
|
|
1133
|
+
"""Four Wing-3 GTM cognition-boundary checks.
|
|
1134
|
+
|
|
1135
|
+
Triggered when a skill's ``context_spine`` declares at least one
|
|
1136
|
+
Wing-3 slot (channel-stage, funnel-stage, customer-segment). Enforces
|
|
1137
|
+
council Q7 / iter-2 OQ3 verdict that GTM cognition stays:
|
|
1138
|
+
|
|
1139
|
+
- **agent-operability** — no external SaaS URLs the agent would auth against.
|
|
1140
|
+
- **vendor-independence** — no platform / SDK / brand slugs.
|
|
1141
|
+
- **transferability** — no stack-locked tooling instructions.
|
|
1142
|
+
- **channel-agnosticism** — no channel-specific tactical prescriptions.
|
|
1143
|
+
|
|
1144
|
+
Carve-outs: fenced code, inline backticks, the ``## Do NOT`` block,
|
|
1145
|
+
and ``**WHEN NOT to use this**`` lists — so authors can cite a vendor
|
|
1146
|
+
as off-scope without tripping the linter.
|
|
1147
|
+
"""
|
|
1148
|
+
issues: List[Issue] = []
|
|
1149
|
+
body = _strip_wing3_carve_outs(text)
|
|
1150
|
+
|
|
1151
|
+
match = WING3_SAAS_URL_PATTERN.search(body)
|
|
1152
|
+
if match:
|
|
1153
|
+
issues.append(Issue(
|
|
1154
|
+
"warning", "wing3_agent_operability",
|
|
1155
|
+
f"Wing-3 skill cites external SaaS URL `{match.group(0)}` outside "
|
|
1156
|
+
f"carve-outs — cognition skills must operate without SaaS auth "
|
|
1157
|
+
f"(council Q7 boundary)",
|
|
1158
|
+
))
|
|
1159
|
+
|
|
1160
|
+
match = WING3_VENDOR_BLACKLIST.search(body)
|
|
1161
|
+
if match:
|
|
1162
|
+
issues.append(Issue(
|
|
1163
|
+
"warning", "wing3_vendor_independence",
|
|
1164
|
+
f"Wing-3 skill names vendor `{match.group(0)}` outside carve-outs "
|
|
1165
|
+
f"— keep cognition vendor-agnostic (council Q7 boundary)",
|
|
1166
|
+
))
|
|
1167
|
+
|
|
1168
|
+
match = WING3_STACK_LOCKED_PATTERN.search(body)
|
|
1169
|
+
if match:
|
|
1170
|
+
issues.append(Issue(
|
|
1171
|
+
"warning", "wing3_transferability",
|
|
1172
|
+
f"Wing-3 skill includes stack-locked instruction `{match.group(0)}` "
|
|
1173
|
+
f"outside carve-outs — cognition should transfer across stacks "
|
|
1174
|
+
f"(council Q7 boundary)",
|
|
1175
|
+
))
|
|
1176
|
+
|
|
1177
|
+
match = WING3_CHANNEL_TACTIC_PATTERN.search(body)
|
|
1178
|
+
if match:
|
|
1179
|
+
issues.append(Issue(
|
|
1180
|
+
"warning", "wing3_channel_agnosticism",
|
|
1181
|
+
f"Wing-3 skill prescribes channel-specific tactic "
|
|
1182
|
+
f"`{match.group(0)}` outside carve-outs — keep cognition "
|
|
1183
|
+
f"channel-agnostic (council Q7 boundary)",
|
|
1184
|
+
))
|
|
1185
|
+
|
|
1186
|
+
return issues
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
def lint_wing4_boundaries(text: str) -> List[Issue]:
|
|
1190
|
+
"""Four Wing-4 Money/Strategy/Ops cognition-boundary checks.
|
|
1191
|
+
|
|
1192
|
+
Triggered when a skill's ``context_spine`` declares at least one
|
|
1193
|
+
Wing-4 slot (fiscal-period, org-stage, regulatory-regime). Enforces
|
|
1194
|
+
council Q7 / J2 verdict that Money/Strategy/Ops cognition stays:
|
|
1195
|
+
|
|
1196
|
+
- **agent-operability** — no external finance/HR/legal SaaS URLs.
|
|
1197
|
+
- **vendor-independence** — no QuickBooks/Carta/Gusto-class brand slugs.
|
|
1198
|
+
- **transferability** — no stack-locked tooling instructions.
|
|
1199
|
+
- **stage-agnosticism** — no prescriptive stage-specific thresholds.
|
|
1200
|
+
|
|
1201
|
+
Carve-outs are identical to Wing-3: fenced code, inline backticks,
|
|
1202
|
+
the ``## Do NOT`` block, and ``**WHEN NOT to use this**`` lists.
|
|
1203
|
+
Regulatory regime names (GDPR / HIPAA / SOC2 / PCI / CCPA) are
|
|
1204
|
+
cognition-relevant constraints, not vendors — they pass.
|
|
1205
|
+
"""
|
|
1206
|
+
issues: List[Issue] = []
|
|
1207
|
+
body = _strip_wing3_carve_outs(text)
|
|
1208
|
+
|
|
1209
|
+
match = WING4_SAAS_URL_PATTERN.search(body)
|
|
1210
|
+
if match:
|
|
1211
|
+
issues.append(Issue(
|
|
1212
|
+
"warning", "wing4_agent_operability",
|
|
1213
|
+
f"Wing-4 skill cites external SaaS URL `{match.group(0)}` outside "
|
|
1214
|
+
f"carve-outs — cognition skills must operate without SaaS auth "
|
|
1215
|
+
f"(council Q7 boundary)",
|
|
1216
|
+
))
|
|
1217
|
+
|
|
1218
|
+
match = WING4_VENDOR_BLACKLIST.search(body)
|
|
1219
|
+
if match:
|
|
1220
|
+
issues.append(Issue(
|
|
1221
|
+
"warning", "wing4_vendor_independence",
|
|
1222
|
+
f"Wing-4 skill names vendor `{match.group(0)}` outside carve-outs "
|
|
1223
|
+
f"— keep cognition vendor-agnostic (council Q7 boundary)",
|
|
1224
|
+
))
|
|
1225
|
+
|
|
1226
|
+
match = WING3_STACK_LOCKED_PATTERN.search(body)
|
|
1227
|
+
if match:
|
|
1228
|
+
issues.append(Issue(
|
|
1229
|
+
"warning", "wing4_transferability",
|
|
1230
|
+
f"Wing-4 skill includes stack-locked instruction `{match.group(0)}` "
|
|
1231
|
+
f"outside carve-outs — cognition should transfer across stacks "
|
|
1232
|
+
f"(council Q7 boundary)",
|
|
1233
|
+
))
|
|
1234
|
+
|
|
1235
|
+
match = WING4_STAGE_AGNOSTIC_PATTERN.search(body)
|
|
1236
|
+
if match:
|
|
1237
|
+
issues.append(Issue(
|
|
1238
|
+
"warning", "wing4_stage_agnosticism",
|
|
1239
|
+
f"Wing-4 skill prescribes stage-locked threshold "
|
|
1240
|
+
f"`{match.group(0)}` outside carve-outs — cognition must "
|
|
1241
|
+
f"transfer across seed and public (council Q7 boundary)",
|
|
1242
|
+
))
|
|
1243
|
+
|
|
1244
|
+
return issues
|
|
1245
|
+
|
|
1246
|
+
|
|
996
1247
|
def lint_execution_metadata(execution: dict) -> List[Issue]:
|
|
997
1248
|
"""Validate the execution block of a skill."""
|
|
998
1249
|
issues: List[Issue] = []
|
|
@@ -1436,6 +1687,27 @@ def lint_persona(path: Path, text: str) -> LintResult:
|
|
|
1436
1687
|
f"Persona tier `{parsed['tier']}` must be one of {sorted(VALID_PERSONA_TIERS)}",
|
|
1437
1688
|
))
|
|
1438
1689
|
|
|
1690
|
+
# wing — optional; when present must be one of {1,2,3,4} (per
|
|
1691
|
+
# docs/contracts/package-self-orientation.md § The four wings).
|
|
1692
|
+
wing_match = re.search(r'^wing:\s*"?(\d+)"?\s*$', frontmatter, re.MULTILINE)
|
|
1693
|
+
if wing_match:
|
|
1694
|
+
try:
|
|
1695
|
+
wing_value = int(wing_match.group(1))
|
|
1696
|
+
if wing_value in VALID_PERSONA_WINGS:
|
|
1697
|
+
parsed["wing"] = wing_value
|
|
1698
|
+
else:
|
|
1699
|
+
issues.append(Issue(
|
|
1700
|
+
"error",
|
|
1701
|
+
"invalid_wing",
|
|
1702
|
+
f"Persona wing `{wing_value}` must be one of {sorted(VALID_PERSONA_WINGS)}",
|
|
1703
|
+
))
|
|
1704
|
+
except ValueError:
|
|
1705
|
+
issues.append(Issue(
|
|
1706
|
+
"error",
|
|
1707
|
+
"invalid_wing",
|
|
1708
|
+
f"Persona wing `{wing_match.group(1)}` must be an integer 1–4",
|
|
1709
|
+
))
|
|
1710
|
+
|
|
1439
1711
|
# description length
|
|
1440
1712
|
if "description" in parsed and len(parsed["description"]) > 160:
|
|
1441
1713
|
issues.append(Issue(
|
|
@@ -1473,15 +1745,21 @@ def lint_persona(path: Path, text: str) -> LintResult:
|
|
|
1473
1745
|
f"Persona has {bullet_count} unique questions (target ≥ 3)",
|
|
1474
1746
|
))
|
|
1475
1747
|
|
|
1476
|
-
# Size budget by tier
|
|
1748
|
+
# Size budget by tier — wing-overrides apply when the persona declares a
|
|
1749
|
+
# `wing:` field; defaults to the tier baseline otherwise.
|
|
1477
1750
|
if "tier" in parsed and parsed["tier"] in PERSONA_LINE_BUDGETS:
|
|
1478
|
-
|
|
1751
|
+
tier_value = parsed["tier"]
|
|
1752
|
+
wing_value = parsed.get("wing")
|
|
1753
|
+
budget = PERSONA_LINE_BUDGETS_BY_WING.get(
|
|
1754
|
+
(tier_value, wing_value), PERSONA_LINE_BUDGETS[tier_value]
|
|
1755
|
+
)
|
|
1479
1756
|
line_count = len(text.splitlines())
|
|
1480
1757
|
if line_count > budget:
|
|
1758
|
+
scope = f"{tier_value}" if wing_value is None else f"{tier_value}, wing {wing_value}"
|
|
1481
1759
|
issues.append(Issue(
|
|
1482
1760
|
"warning",
|
|
1483
1761
|
"size_budget",
|
|
1484
|
-
f"Persona has {line_count} lines ({
|
|
1762
|
+
f"Persona has {line_count} lines ({scope} budget ≤ {budget})",
|
|
1485
1763
|
))
|
|
1486
1764
|
|
|
1487
1765
|
# H1 heading
|