taketomarket 0.1.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/.claude-plugin/plugin.json +10 -0
- package/LICENSE +21 -0
- package/README.md +419 -0
- package/agents/ttm-producer.md +53 -0
- package/bin/lib/campaign.cjs +553 -0
- package/bin/lib/commit.cjs +105 -0
- package/bin/lib/core.cjs +172 -0
- package/bin/lib/deviation.cjs +149 -0
- package/bin/lib/drift-log.cjs +219 -0
- package/bin/lib/health.cjs +438 -0
- package/bin/lib/slug.cjs +59 -0
- package/bin/lib/state.cjs +96 -0
- package/bin/ttm-tools.cjs +157 -0
- package/gates/base-gates.md +266 -0
- package/gates/discipline/.gitkeep +0 -0
- package/gates/gate-evaluation.md +341 -0
- package/gates/meta-gates.md +19 -0
- package/install.js +307 -0
- package/package.json +53 -0
- package/playbooks/.gitkeep +0 -0
- package/playbooks/aeo.md +223 -0
- package/playbooks/affiliate.md +272 -0
- package/playbooks/base.md +110 -0
- package/playbooks/email.md +306 -0
- package/playbooks/events.md +320 -0
- package/playbooks/linkedin.md +263 -0
- package/playbooks/paid-ads.md +318 -0
- package/playbooks/pr-media.md +296 -0
- package/playbooks/seo.md +284 -0
- package/playbooks/social.md +305 -0
- package/playbooks/youtube.md +325 -0
- package/references/context-loading.md +107 -0
- package/references/learnings-extraction.md +94 -0
- package/references/measurement-template.md +48 -0
- package/references/meta-gate-evaluation.md +169 -0
- package/references/positioning-check-report.md +197 -0
- package/references/review-checklist.md +78 -0
- package/references/ship-checklist-items.md +94 -0
- package/settings.json +4 -0
- package/skills/ttm-aeo-check/SKILL.md +20 -0
- package/skills/ttm-affiliate-kit/SKILL.md +19 -0
- package/skills/ttm-archive/SKILL.md +13 -0
- package/skills/ttm-brand-refresh/SKILL.md +19 -0
- package/skills/ttm-brief/SKILL.md +14 -0
- package/skills/ttm-competitor-scan/SKILL.md +19 -0
- package/skills/ttm-email-preflight/SKILL.md +19 -0
- package/skills/ttm-fix/SKILL.md +13 -0
- package/skills/ttm-health/SKILL.md +12 -0
- package/skills/ttm-icp-refresh/SKILL.md +19 -0
- package/skills/ttm-init/SKILL.md +12 -0
- package/skills/ttm-keyword-map/SKILL.md +19 -0
- package/skills/ttm-learn/SKILL.md +14 -0
- package/skills/ttm-measure/SKILL.md +14 -0
- package/skills/ttm-new-campaign/SKILL.md +13 -0
- package/skills/ttm-next/SKILL.md +12 -0
- package/skills/ttm-positioning-check/SKILL.md +19 -0
- package/skills/ttm-positioning-shift/SKILL.md +19 -0
- package/skills/ttm-produce/SKILL.md +14 -0
- package/skills/ttm-repurpose/SKILL.md +20 -0
- package/skills/ttm-research/SKILL.md +13 -0
- package/skills/ttm-resume/SKILL.md +13 -0
- package/skills/ttm-review/SKILL.md +13 -0
- package/skills/ttm-seo-audit/SKILL.md +20 -0
- package/skills/ttm-ship/SKILL.md +13 -0
- package/skills/ttm-state/SKILL.md +13 -0
- package/skills/ttm-verify/SKILL.md +14 -0
- package/templates/agents-md.md +65 -0
- package/templates/campaign-brief.md +74 -0
- package/templates/campaign-research.md +39 -0
- package/templates/campaign-state.md +40 -0
- package/templates/claude-md.md +65 -0
- package/templates/deviation-log.md +12 -0
- package/templates/drift-log.md +17 -0
- package/templates/fix-brief.md +59 -0
- package/templates/fix-log.md +22 -0
- package/templates/measurement-report.md +75 -0
- package/templates/migration-plan.md +24 -0
- package/templates/production-manifest.json +20 -0
- package/templates/reference-files/brand.md +45 -0
- package/templates/reference-files/calendar.md +30 -0
- package/templates/reference-files/channels.md +40 -0
- package/templates/reference-files/competitors.md +40 -0
- package/templates/reference-files/icp.md +50 -0
- package/templates/reference-files/learnings.md +40 -0
- package/templates/reference-files/metrics.md +42 -0
- package/templates/reference-files/positioning.md +38 -0
- package/templates/reference-files/state.md +27 -0
- package/templates/verification-report.md +59 -0
- package/workflows/discipline/.gitkeep +0 -0
- package/workflows/discipline/aeo-check.md +180 -0
- package/workflows/discipline/affiliate-kit.md +147 -0
- package/workflows/discipline/email-preflight.md +150 -0
- package/workflows/discipline/keyword-map.md +125 -0
- package/workflows/discipline/repurpose.md +329 -0
- package/workflows/discipline/seo-audit.md +169 -0
- package/workflows/lifecycle/.gitkeep +0 -0
- package/workflows/lifecycle/brief-positioning-check.md +90 -0
- package/workflows/lifecycle/brief.md +355 -0
- package/workflows/lifecycle/fix.md +495 -0
- package/workflows/lifecycle/learn.md +405 -0
- package/workflows/lifecycle/measure.md +379 -0
- package/workflows/lifecycle/produce.md +383 -0
- package/workflows/lifecycle/research.md +264 -0
- package/workflows/lifecycle/review.md +432 -0
- package/workflows/lifecycle/ship.md +521 -0
- package/workflows/lifecycle/verify.md +507 -0
- package/workflows/reference-mgmt/.gitkeep +0 -0
- package/workflows/reference-mgmt/brand-refresh.md +193 -0
- package/workflows/reference-mgmt/competitor-scan.md +228 -0
- package/workflows/reference-mgmt/icp-refresh.md +200 -0
- package/workflows/reference-mgmt/positioning-check.md +339 -0
- package/workflows/reference-mgmt/positioning-shift.md +368 -0
- package/workflows/setup/.gitkeep +0 -0
- package/workflows/setup/init-questions.md +225 -0
- package/workflows/setup/init-validation.md +155 -0
- package/workflows/setup/init.md +449 -0
- package/workflows/setup/new-campaign.md +134 -0
- package/workflows/utility/.gitkeep +0 -0
- package/workflows/utility/archive.md +334 -0
- package/workflows/utility/health.md +166 -0
- package/workflows/utility/next.md +187 -0
- package/workflows/utility/resume.md +249 -0
- package/workflows/utility/state.md +207 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
# Gate Evaluation Reference
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
Referenced by `workflows/lifecycle/verify.md` via @-syntax.
|
|
6
|
+
Applied to each asset during verification. Each gate is evaluated independently
|
|
7
|
+
with its specific reference data loaded.
|
|
8
|
+
|
|
9
|
+
**Key rules:**
|
|
10
|
+
- Evaluate gates one at a time, not all bundled in a single prompt (prevents shallow evaluation)
|
|
11
|
+
- Load the gate's reference file(s) for each evaluation
|
|
12
|
+
- Use the structured output format below for every finding
|
|
13
|
+
- Soft fail on all gates -- report results, never block (per D-04)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Structured Output Format
|
|
18
|
+
|
|
19
|
+
Every gate evaluation MUST produce output in this exact format:
|
|
20
|
+
|
|
21
|
+
### Gate Result
|
|
22
|
+
|
|
23
|
+
- **Gate:** [Gate Name] (GATE-XX)
|
|
24
|
+
- **Tier:** [1|2]
|
|
25
|
+
- **Result:** [PASS|WARN|FAIL]
|
|
26
|
+
- **Summary:** [One-sentence summary of the finding]
|
|
27
|
+
|
|
28
|
+
### Findings
|
|
29
|
+
|
|
30
|
+
For each check within the gate:
|
|
31
|
+
|
|
32
|
+
**Check: [check name]**
|
|
33
|
+
- **Result:** PASS | WARN | FAIL
|
|
34
|
+
- **Evidence:** "[exact quote from the asset that triggered this finding]" (line/section reference)
|
|
35
|
+
- **Reference:** "[quote from the reference file being compared against]"
|
|
36
|
+
- **Recommendation:** [what to change if WARN or FAIL; "None" if PASS]
|
|
37
|
+
|
|
38
|
+
If a check is N/A (e.g., UTM hygiene for an asset without links), record:
|
|
39
|
+
- **Result:** N/A
|
|
40
|
+
- **Evidence:** "Not applicable -- [reason]"
|
|
41
|
+
- **Reference:** N/A
|
|
42
|
+
- **Recommendation:** None
|
|
43
|
+
|
|
44
|
+
### Aggregation Rule
|
|
45
|
+
|
|
46
|
+
- If ALL checks PASS: gate result = PASS
|
|
47
|
+
- If ANY check is WARN and none FAIL: gate result = WARN
|
|
48
|
+
- If ANY check is FAIL: gate result = FAIL
|
|
49
|
+
- N/A checks are excluded from aggregation
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Per-Gate Evaluation Instructions
|
|
54
|
+
|
|
55
|
+
### Evaluating GATE-01: Positioning Drift
|
|
56
|
+
|
|
57
|
+
**Load:** `.marketing/POSITIONING.md` (Tier 2 full)
|
|
58
|
+
**Asset content:** Full asset text
|
|
59
|
+
|
|
60
|
+
**Evaluate:**
|
|
61
|
+
1. Does the asset restate or naturally extend the primary differentiator from POSITIONING.md? Or does it introduce a different claim?
|
|
62
|
+
2. Are all factual claims in the asset backed by proof points in the POSITIONING.md proof point library?
|
|
63
|
+
3. Does the asset contain any terms from the POSITIONING.md must-not-say list?
|
|
64
|
+
|
|
65
|
+
**Tier:** 1
|
|
66
|
+
**On failure:** Prompt user for Correct / Accept+log / Escalate
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### Evaluating GATE-02: Claim Accuracy
|
|
71
|
+
|
|
72
|
+
**Load:** `.marketing/BRAND.md` (Tier 2 full -- proof points section)
|
|
73
|
+
**Asset content:** Full asset text
|
|
74
|
+
|
|
75
|
+
**Evaluate:**
|
|
76
|
+
1. For every factual or numeric claim in the asset, find the matching proof point in BRAND.md. If none exists, record FAIL.
|
|
77
|
+
2. Where proof points are used, is the source cited or at least implied? Record WARN if implied only, FAIL if no attribution.
|
|
78
|
+
3. Check each proof point used against BRAND.md for deprecation or expiration markers.
|
|
79
|
+
|
|
80
|
+
**Tier:** 1
|
|
81
|
+
**On failure:** Prompt user for Correct / Accept+log / Escalate
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### Evaluating GATE-03: Voice Drift
|
|
86
|
+
|
|
87
|
+
**Load:** `.marketing/BRAND.md` (Tier 2 full -- voice archetype and banned words sections)
|
|
88
|
+
**Asset content:** Full asset text
|
|
89
|
+
|
|
90
|
+
**Evaluate:**
|
|
91
|
+
1. Read the voice archetype description in BRAND.md. Does the asset's overall tone match? Flag sections that diverge.
|
|
92
|
+
2. Scan the asset for every word in the BRAND.md banned words list. Any match is a FAIL.
|
|
93
|
+
3. Read the asset start to end. Does the language register stay consistent, or does it shift between formal and casual?
|
|
94
|
+
|
|
95
|
+
**Tier:** 2
|
|
96
|
+
**On failure:** Report as advisory
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Evaluating GATE-04: Outcome Alignment
|
|
101
|
+
|
|
102
|
+
**Load:** `.marketing/CAMPAIGNS/<slug>/BRIEF.md` (outcome metric section)
|
|
103
|
+
**Asset content:** Full asset text
|
|
104
|
+
|
|
105
|
+
**Evaluate:**
|
|
106
|
+
1. What is the outcome metric in the brief? Does this asset drive that metric, or does it only produce output (impressions, clicks) without a path to the outcome?
|
|
107
|
+
2. Does the CTA or desired reader action serve the outcome metric directly, or does it target a tangential goal?
|
|
108
|
+
|
|
109
|
+
**Tier:** 1
|
|
110
|
+
**On failure:** Prompt user for Correct / Accept+log / Escalate
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### Evaluating GATE-05: Funnel Integrity
|
|
115
|
+
|
|
116
|
+
**Load:** `.marketing/CAMPAIGNS/<slug>/BRIEF.md` (funnel/CTA section)
|
|
117
|
+
**Asset content:** Full asset text
|
|
118
|
+
|
|
119
|
+
**Evaluate:**
|
|
120
|
+
1. Is a CTA present? Is it specific (e.g., "Start your free trial") rather than generic ("Learn more")? If the asset type does not require a CTA, mark N/A.
|
|
121
|
+
2. Is the CTA destination defined (URL, landing page, next step)? Is it a dead end?
|
|
122
|
+
3. Trace the path from CTA to outcome. Is it logical and unbroken, or are there gaps or unnecessary friction?
|
|
123
|
+
|
|
124
|
+
**Tier:** 2
|
|
125
|
+
**On failure:** Report as advisory
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
### Evaluating GATE-06: UTM Hygiene
|
|
130
|
+
|
|
131
|
+
**Load:** `.marketing/CHANNELS.md` (UTM schema section)
|
|
132
|
+
**Asset content:** Full asset text
|
|
133
|
+
|
|
134
|
+
**Evaluate:**
|
|
135
|
+
1. Find all trackable links in the asset. Do they have UTM parameters (source, medium, campaign)? If no links exist, mark all checks N/A.
|
|
136
|
+
2. Do UTM source and medium values match the naming conventions in CHANNELS.md?
|
|
137
|
+
3. Does the UTM campaign tag match the campaign slug?
|
|
138
|
+
|
|
139
|
+
**Tier:** 2
|
|
140
|
+
**On failure:** Report as advisory
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### Evaluating GATE-07: Compliance
|
|
145
|
+
|
|
146
|
+
**Load:** No specific reference file -- apply industry-standard requirements
|
|
147
|
+
**Asset content:** Full asset text + channel context
|
|
148
|
+
|
|
149
|
+
**Evaluate:**
|
|
150
|
+
1. Based on the asset content, are disclaimers required (financial advice, health claims, affiliate disclosure, sponsorship)? If required, are they present? If not required, mark N/A.
|
|
151
|
+
2. Scan the asset for PII patterns (email addresses, phone numbers, personal names of non-public figures).
|
|
152
|
+
3. For email, SMS, or push notification assets: is an unsubscribe/opt-out mechanism referenced? For other channels, mark N/A.
|
|
153
|
+
|
|
154
|
+
**Tier:** 2
|
|
155
|
+
**On failure:** Report as advisory
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### Evaluating GATE-08: Competitor Collision
|
|
160
|
+
|
|
161
|
+
**Load:** `.marketing/COMPETITORS.md` (Tier 2 full)
|
|
162
|
+
**Asset content:** Full asset text
|
|
163
|
+
|
|
164
|
+
**Evaluate:**
|
|
165
|
+
1. Does the asset mention any competitor brand names from COMPETITORS.md? If so, are the mentions accompanied by substantiated comparative claims?
|
|
166
|
+
2. Compare the asset's framing and key phrases against competitor positioning and taglines listed in COMPETITORS.md. Flag overlaps.
|
|
167
|
+
3. Does the asset differentiate from competitors, or does it inadvertently validate a competitor's approach or market framing?
|
|
168
|
+
|
|
169
|
+
**Tier:** 2
|
|
170
|
+
**On failure:** Report as advisory
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### Evaluating GATE-09: ICP Fit
|
|
175
|
+
|
|
176
|
+
**Load:** `.marketing/ICP.md` (Tier 2 full)
|
|
177
|
+
**Asset content:** Full asset text
|
|
178
|
+
|
|
179
|
+
**Evaluate:**
|
|
180
|
+
1. Does the hook or core message address a specific pain point from ICP.md pains list?
|
|
181
|
+
2. Does the asset use vocabulary from ICP.md's language library, or does it use internal jargon the ICP would not recognize?
|
|
182
|
+
3. Is the content targeted at the ICP segment, or could it primarily attract the anti-ICP defined in ICP.md?
|
|
183
|
+
|
|
184
|
+
**Tier:** 2
|
|
185
|
+
**On failure:** Report as advisory
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### Evaluating GATE-10: Format Correctness
|
|
190
|
+
|
|
191
|
+
**Load:** Channel-specific playbook if available (`${CLAUDE_PLUGIN_ROOT}/playbooks/<type>.md`), otherwise general platform guidelines
|
|
192
|
+
**Asset content:** Full asset text + asset type metadata from MANIFEST.json
|
|
193
|
+
|
|
194
|
+
**Evaluate:**
|
|
195
|
+
1. Check asset length against platform limits (tweet < 280 chars, LinkedIn < 3000, email subject < 60). If no specific limit applies, check general word count reasonableness.
|
|
196
|
+
2. Check for required structural elements: subject line (email), H1/title (blog), hook (social), preview text (email). Flag missing elements.
|
|
197
|
+
3. Does the asset structure follow channel conventions (e.g., email has preview text and CTA above fold, blog has meta description, social post uses hashtags where expected)?
|
|
198
|
+
|
|
199
|
+
**Tier:** 2
|
|
200
|
+
**On failure:** Report as advisory
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### Evaluating Discipline Gates (DISC-*)
|
|
205
|
+
|
|
206
|
+
**Load:** Playbook file for this asset type (`${CLAUDE_PLUGIN_ROOT}/playbooks/<type>.md`)
|
|
207
|
+
**Asset content:** Full asset text
|
|
208
|
+
|
|
209
|
+
**Evaluate:**
|
|
210
|
+
For each `### DISC-*` subsection in the playbook's `## Discipline Gates` section:
|
|
211
|
+
|
|
212
|
+
1. Read the gate's **Checks** and **Against** fields to identify what is being evaluated and the reference data
|
|
213
|
+
2. Evaluate each numbered criterion under **Evaluation Criteria** against the asset content
|
|
214
|
+
3. Apply the same PASS/WARN/FAIL assessment and structured output format as base gates (see Structured Output Format above)
|
|
215
|
+
4. Use the tier specified in the discipline gate definition (from the `-- Tier {1|2}` in the heading)
|
|
216
|
+
|
|
217
|
+
**On failure:** Apply deviation handling based on the gate's tier:
|
|
218
|
+
- Tier 1 discipline gates: Prompt user for Correct / Accept+log / Escalate (same as base Tier 1)
|
|
219
|
+
- Tier 2 discipline gates: Report as advisory (same as base Tier 2)
|
|
220
|
+
|
|
221
|
+
**Aggregation:** Same rule as base gates:
|
|
222
|
+
- If ALL checks PASS: gate result = PASS
|
|
223
|
+
- If ANY check is WARN and none FAIL: gate result = WARN
|
|
224
|
+
- If ANY check is FAIL: gate result = FAIL
|
|
225
|
+
- N/A checks are excluded from aggregation
|
|
226
|
+
|
|
227
|
+
**Key rules:**
|
|
228
|
+
- Evaluate each discipline gate separately (same as base gates -- no bundling)
|
|
229
|
+
- Discipline gates must not duplicate base gate checks
|
|
230
|
+
- If the asset has no playbook, skip discipline gate evaluation entirely
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Deviation Handling (GATE-12)
|
|
235
|
+
|
|
236
|
+
When a gate results in WARN or FAIL:
|
|
237
|
+
|
|
238
|
+
### Tier 1 Failures (GATE-01, GATE-02, GATE-04, base gates overridden to Tier 1, and Tier 1 DISC-* gates)
|
|
239
|
+
|
|
240
|
+
Present the finding and prompt for action:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
Gate [name] returned [WARN|FAIL]:
|
|
244
|
+
[finding summary with evidence]
|
|
245
|
+
|
|
246
|
+
Choose an action:
|
|
247
|
+
1. Correct -- Record this for /ttm-fix to address
|
|
248
|
+
2. Accept+log -- Document exception and proceed
|
|
249
|
+
3. Escalate -- Launch /ttm-positioning-shift to update positioning
|
|
250
|
+
|
|
251
|
+
Type 1, 2, or 3:
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Text-mode fallback:** When `TEXT_MODE=true`, present the same numbered list as plain text
|
|
255
|
+
and read the user's response from the next message.
|
|
256
|
+
|
|
257
|
+
### Tier 2 Findings (GATE-03, GATE-05 through GATE-10 unless overridden, and Tier 2 DISC-* gates)
|
|
258
|
+
|
|
259
|
+
Report findings without requiring action:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
[Advisory] Gate [name]: [finding summary]
|
|
263
|
+
Recommendation: [recommendation from finding]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
User may optionally request action on any Tier 2 finding.
|
|
267
|
+
|
|
268
|
+
### Accept+log Record Format (D-07)
|
|
269
|
+
|
|
270
|
+
When user selects Accept+log, collect justification via AskUserQuestion:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
Why are you accepting this deviation? (This will be logged to DEVIATIONS.md)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Record the deviation using the CLI:
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" deviation append \
|
|
280
|
+
--slug "${SLUG}" \
|
|
281
|
+
--gate "[gate name]" \
|
|
282
|
+
--gate-id "[GATE-XX]" \
|
|
283
|
+
--tier [1|2] \
|
|
284
|
+
--result "[WARN|FAIL]" \
|
|
285
|
+
--asset "[asset file path]" \
|
|
286
|
+
--finding "[exact finding text]" \
|
|
287
|
+
--action "Accept+log" \
|
|
288
|
+
--justification "[user's response]" \
|
|
289
|
+
--run [current run number]
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
This writes a consistent entry to DEVIATIONS.md (append-only) and updates STATE.md gate field.
|
|
293
|
+
|
|
294
|
+
### Escalate Behavior (D-08)
|
|
295
|
+
|
|
296
|
+
When user selects Escalate:
|
|
297
|
+
- Pause verification
|
|
298
|
+
- Display: "Launching /ttm-positioning-shift. Verification paused."
|
|
299
|
+
- After positioning resolution: "Positioning updated. Re-run /ttm-verify to validate with new positioning."
|
|
300
|
+
|
|
301
|
+
### Correct Behavior (D-09)
|
|
302
|
+
|
|
303
|
+
When user selects Correct:
|
|
304
|
+
- Record the finding as needing fix
|
|
305
|
+
- Update campaign STATE.md via CLI:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
node "${CLAUDE_PLUGIN_ROOT}/bin/ttm-tools.cjs" campaign update "${SLUG}" gate.[gate_field] "fix_needed"
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- Display: "Recorded for /ttm-fix. Continue verifying remaining gates."
|
|
312
|
+
- Verification continues -- does not pause for Correct. Fix happens after verify completes.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Summary Table Format (D-05)
|
|
317
|
+
|
|
318
|
+
After all gates are evaluated for all assets, display a summary table:
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
## Verification Report: [campaign slug]
|
|
322
|
+
|
|
323
|
+
**Run:** [N] | **Date:** [ISO date] | **Assets:** [count]
|
|
324
|
+
|
|
325
|
+
| # | Gate | Tier | Asset 1 | Asset 2 | ... |
|
|
326
|
+
|---|------|------|---------|---------|-----|
|
|
327
|
+
| 1 | Positioning Drift | T1 | PASS | WARN | ... |
|
|
328
|
+
| 2 | Claim Accuracy | T1 | PASS | PASS | ... |
|
|
329
|
+
...
|
|
330
|
+
| 10 | Format Correctness | T2 | PASS | PASS | ... |
|
|
331
|
+
| 11 | DISC-{DISC}-01: {Name} | T{n} | PASS | N/A | ... |
|
|
332
|
+
| .. | ... | ... | ... | ... | ... |
|
|
333
|
+
|
|
334
|
+
Discipline gate rows appear after the 10 base gates. For assets without a matching
|
|
335
|
+
playbook, show N/A in that asset's column.
|
|
336
|
+
|
|
337
|
+
**Result:** [count] FAIL, [count] WARN -- [action required | all clear]
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Below the summary table, include drill-down detail for every WARN and FAIL finding
|
|
341
|
+
using the structured output format defined above.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Meta Quality Gates
|
|
2
|
+
|
|
3
|
+
**Status:** Meta-gate definitions will be implemented in Phase 9
|
|
4
|
+
|
|
5
|
+
Meta-gates operate at the portfolio level, not on individual assets. They evaluate campaigns and content programs as a whole.
|
|
6
|
+
|
|
7
|
+
## Meta-Gates
|
|
8
|
+
|
|
9
|
+
1. **Portfolio Balance** -- META-01
|
|
10
|
+
Evaluates whether the active campaign mix covers all funnel stages (awareness, consideration, conversion, retention) and does not over-index on any single stage.
|
|
11
|
+
|
|
12
|
+
2. **Calendar Collision** -- META-02
|
|
13
|
+
Detects scheduling conflicts between campaigns: overlapping launch dates, competing for the same audience segment simultaneously, or conflicting messages in market at the same time.
|
|
14
|
+
|
|
15
|
+
3. **Theme Consistency** -- META-03
|
|
16
|
+
Verifies active campaigns align with the quarterly theme defined in CALENDAR.md. Flags campaigns that drift from the strategic narrative.
|
|
17
|
+
|
|
18
|
+
4. **Learning Plan** -- META-04
|
|
19
|
+
Ensures every campaign has a measurement plan and a learning hypothesis. Flags campaigns that ship without defining what success looks like or what the team will learn.
|
package/install.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const os = require('os');
|
|
8
|
+
|
|
9
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const PACKAGE_ROOT = __dirname;
|
|
12
|
+
const VERSION = require('./package.json').version;
|
|
13
|
+
|
|
14
|
+
const DIRS_TO_COPY = [
|
|
15
|
+
'.claude-plugin',
|
|
16
|
+
'skills',
|
|
17
|
+
'workflows',
|
|
18
|
+
'templates',
|
|
19
|
+
'references',
|
|
20
|
+
'playbooks',
|
|
21
|
+
'gates',
|
|
22
|
+
'bin',
|
|
23
|
+
'agents',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
const FILES_TO_COPY = [
|
|
27
|
+
'settings.json',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// ── Runtime detection ────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detect target runtime from CLI args or environment sniffing.
|
|
34
|
+
* Priority: --runtime flag > .claude/ dir > .codex/ dir > default claude
|
|
35
|
+
* @param {string[]} args - CLI arguments
|
|
36
|
+
* @returns {string} 'claude' or 'codex'
|
|
37
|
+
*/
|
|
38
|
+
function detectRuntime(args) {
|
|
39
|
+
// Check --runtime flag
|
|
40
|
+
const runtimeIdx = args.indexOf('--runtime');
|
|
41
|
+
if (runtimeIdx !== -1 && runtimeIdx + 1 < args.length) {
|
|
42
|
+
const value = args[runtimeIdx + 1].toLowerCase();
|
|
43
|
+
if (value === 'claude' || value === 'codex') {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
console.warn(`Warning: Unknown runtime "${args[runtimeIdx + 1]}". Defaulting to claude.`);
|
|
47
|
+
return 'claude';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check for .claude/ directory
|
|
51
|
+
if (dirExists(path.join(os.homedir(), '.claude'))) {
|
|
52
|
+
return 'claude';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check for .codex/ directory
|
|
56
|
+
if (dirExists(path.join(os.homedir(), '.codex'))) {
|
|
57
|
+
return 'codex';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Default
|
|
61
|
+
console.log('Note: Defaulting to Claude Code. Use --runtime codex if using Codex.');
|
|
62
|
+
return 'claude';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── File helpers ─────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Check if a path exists and is a directory.
|
|
69
|
+
* @param {string} p - Path to check
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
function dirExists(p) {
|
|
73
|
+
try {
|
|
74
|
+
return fs.statSync(p).isDirectory();
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if a path exists and is a file.
|
|
82
|
+
* @param {string} p - Path to check
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
function fileExists(p) {
|
|
86
|
+
try {
|
|
87
|
+
return fs.statSync(p).isFile();
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Recursively copy a directory. Skips symlinks with a warning.
|
|
95
|
+
* @param {string} src - Source directory path
|
|
96
|
+
* @param {string} dest - Destination directory path
|
|
97
|
+
*/
|
|
98
|
+
function copyDirSync(src, dest) {
|
|
99
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
100
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
101
|
+
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
const srcPath = path.join(src, entry.name);
|
|
104
|
+
const destPath = path.join(dest, entry.name);
|
|
105
|
+
|
|
106
|
+
if (entry.isSymbolicLink()) {
|
|
107
|
+
console.warn(` Warning: Skipping symlink ${entry.name}`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (entry.isDirectory()) {
|
|
112
|
+
copyDirSync(srcPath, destPath);
|
|
113
|
+
} else if (entry.isFile()) {
|
|
114
|
+
fs.copyFileSync(srcPath, destPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Validation ───────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Validate an installation directory has all required components.
|
|
123
|
+
* @param {string} targetDir - Directory to validate
|
|
124
|
+
* @returns {Array<{name: string, status: string}>} Validation results
|
|
125
|
+
*/
|
|
126
|
+
function validateInstall(targetDir) {
|
|
127
|
+
const results = [];
|
|
128
|
+
|
|
129
|
+
// Check each required directory
|
|
130
|
+
for (const dir of DIRS_TO_COPY) {
|
|
131
|
+
results.push({
|
|
132
|
+
name: dir,
|
|
133
|
+
status: dirExists(path.join(targetDir, dir)) ? 'pass' : 'fail',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check plugin.json exists
|
|
138
|
+
results.push({
|
|
139
|
+
name: 'plugin.json',
|
|
140
|
+
status: fileExists(path.join(targetDir, '.claude-plugin', 'plugin.json')) ? 'pass' : 'fail',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Check at least 5 SKILL.md files exist under skills/
|
|
144
|
+
const skillsDir = path.join(targetDir, 'skills');
|
|
145
|
+
let skillCount = 0;
|
|
146
|
+
if (dirExists(skillsDir)) {
|
|
147
|
+
try {
|
|
148
|
+
const skillDirs = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
149
|
+
for (const entry of skillDirs) {
|
|
150
|
+
if (entry.isDirectory()) {
|
|
151
|
+
const skillFile = path.join(skillsDir, entry.name, 'SKILL.md');
|
|
152
|
+
if (fileExists(skillFile)) {
|
|
153
|
+
skillCount++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
// ignore
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
results.push({
|
|
162
|
+
name: `skills (${skillCount} SKILL.md files)`,
|
|
163
|
+
status: skillCount >= 5 ? 'pass' : 'fail',
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return results;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
170
|
+
|
|
171
|
+
function main() {
|
|
172
|
+
const args = process.argv.slice(2);
|
|
173
|
+
|
|
174
|
+
// Check for --version / -v (D-10, D-11) — short-circuit BEFORE detectRuntime/validation.
|
|
175
|
+
if (args.includes('--version') || args.includes('-v')) {
|
|
176
|
+
process.stdout.write(`${VERSION}\n`);
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check for --help
|
|
181
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
182
|
+
console.log(`
|
|
183
|
+
takeToMarket installer
|
|
184
|
+
|
|
185
|
+
Usage: npx taketomarket [options]
|
|
186
|
+
|
|
187
|
+
Options:
|
|
188
|
+
--runtime <claude|codex> Target runtime (default: auto-detect)
|
|
189
|
+
--dry-run Validate source without writing files
|
|
190
|
+
--help, -h Show this help message
|
|
191
|
+
`);
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const DRY_RUN = args.includes('--dry-run');
|
|
196
|
+
const runtime = detectRuntime(args);
|
|
197
|
+
|
|
198
|
+
// Compute target directory using path.resolve for safety (T-10-01)
|
|
199
|
+
const runtimeDir = runtime === 'codex' ? '.codex' : '.claude';
|
|
200
|
+
const targetDir = path.resolve(os.homedir(), runtimeDir, 'plugins', 'taketomarket');
|
|
201
|
+
|
|
202
|
+
// Verify targetDir is within home directory (T-10-01, T-10-03)
|
|
203
|
+
const homeDir = os.homedir();
|
|
204
|
+
if (!targetDir.startsWith(homeDir + path.sep)) {
|
|
205
|
+
console.error('Error: Target directory resolves outside home directory. Aborting.');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log('');
|
|
210
|
+
console.log(`takeToMarket installer v${VERSION}`);
|
|
211
|
+
console.log(`Runtime: ${runtime}`);
|
|
212
|
+
console.log(`Target: ${targetDir}`);
|
|
213
|
+
console.log('');
|
|
214
|
+
|
|
215
|
+
if (DRY_RUN) {
|
|
216
|
+
// Validate source completeness without writing
|
|
217
|
+
console.log('[DRY RUN] Validating source package...');
|
|
218
|
+
console.log('');
|
|
219
|
+
const results = validateInstall(PACKAGE_ROOT);
|
|
220
|
+
printResults(results);
|
|
221
|
+
console.log('');
|
|
222
|
+
console.log('[DRY RUN] No files written.');
|
|
223
|
+
process.exit(0);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check for existing installation — remove stale files before copying (CR-02)
|
|
227
|
+
if (dirExists(targetDir)) {
|
|
228
|
+
console.log('Existing installation found. Removing before reinstall...');
|
|
229
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
230
|
+
console.log('');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Copy directories
|
|
234
|
+
for (const dir of DIRS_TO_COPY) {
|
|
235
|
+
const srcDir = path.join(PACKAGE_ROOT, dir);
|
|
236
|
+
if (dirExists(srcDir)) {
|
|
237
|
+
console.log(` Copying ${dir}/`);
|
|
238
|
+
copyDirSync(srcDir, path.join(targetDir, dir));
|
|
239
|
+
} else {
|
|
240
|
+
console.log(` Skipping ${dir}/ (not found in package)`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Copy individual files
|
|
245
|
+
for (const file of FILES_TO_COPY) {
|
|
246
|
+
const srcFile = path.join(PACKAGE_ROOT, file);
|
|
247
|
+
if (fileExists(srcFile)) {
|
|
248
|
+
console.log(` Copying ${file}`);
|
|
249
|
+
const destFile = path.join(targetDir, file);
|
|
250
|
+
fs.mkdirSync(path.dirname(destFile), { recursive: true });
|
|
251
|
+
fs.copyFileSync(srcFile, destFile);
|
|
252
|
+
} else {
|
|
253
|
+
console.log(` Skipping ${file} (not found in package)`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log('');
|
|
258
|
+
|
|
259
|
+
// Validate
|
|
260
|
+
const results = validateInstall(targetDir);
|
|
261
|
+
printResults(results);
|
|
262
|
+
|
|
263
|
+
const failures = results.filter(r => r.status === 'fail');
|
|
264
|
+
console.log('');
|
|
265
|
+
|
|
266
|
+
if (failures.length > 0) {
|
|
267
|
+
console.log('Installation incomplete. Some components missing.');
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
console.log('Installation complete!');
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log('Quick start:');
|
|
274
|
+
console.log(' 1. Open a project directory');
|
|
275
|
+
console.log(' 2. Run /ttm-init to set up your marketing workspace');
|
|
276
|
+
console.log(' 3. Run /ttm-new-campaign <name> to start your first campaign');
|
|
277
|
+
console.log('');
|
|
278
|
+
console.log('Documentation: https://github.com/taketomarket/taketomarket/blob/main/README.md');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Print validation results as a table.
|
|
283
|
+
* @param {Array<{name: string, status: string}>} results
|
|
284
|
+
*/
|
|
285
|
+
function printResults(results) {
|
|
286
|
+
console.log('Validation:');
|
|
287
|
+
for (const r of results) {
|
|
288
|
+
const label = r.status === 'pass' ? '[PASS]' : '[FAIL]';
|
|
289
|
+
console.log(` ${label} ${r.name}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (require.main === module) {
|
|
294
|
+
main();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = {
|
|
298
|
+
main,
|
|
299
|
+
detectRuntime,
|
|
300
|
+
validateInstall,
|
|
301
|
+
copyDirSync,
|
|
302
|
+
dirExists,
|
|
303
|
+
fileExists,
|
|
304
|
+
printResults,
|
|
305
|
+
DIRS_TO_COPY,
|
|
306
|
+
FILES_TO_COPY,
|
|
307
|
+
};
|