@friedbotstudio/create-baseline 0.5.0 → 0.7.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.
Files changed (55) hide show
  1. package/README.md +14 -10
  2. package/bin/cli.js +46 -15
  3. package/obj/template/.claude/commands/init-project-doctor.md +74 -0
  4. package/obj/template/.claude/hooks/lib/resume_writer.py +14 -1
  5. package/obj/template/.claude/hooks/track_guard.sh +11 -1
  6. package/obj/template/.claude/manifest.json +848 -230
  7. package/obj/template/.claude/schemas/workflow-track.v1.json +64 -0
  8. package/obj/template/.claude/skills/audit-baseline/audit.sh +6 -3
  9. package/obj/template/.claude/skills/chore/SKILL.md +2 -2
  10. package/obj/template/.claude/skills/harness/SKILL.md +15 -6
  11. package/obj/template/.claude/skills/intake/SKILL.md +1 -1
  12. package/obj/template/.claude/skills/swarm-plan/SKILL.md +2 -0
  13. package/obj/template/.claude/skills/tdd/SKILL.md +2 -2
  14. package/obj/template/.claude/skills/triage/SKILL.md +29 -6
  15. package/obj/template/.claude/skills/triage/seed-tasklist.mjs +107 -0
  16. package/obj/template/.claude/skills/upgrade-project/SKILL.md +121 -0
  17. package/obj/template/.claude/workflows.jsonl +6 -0
  18. package/obj/template/CLAUDE.md +14 -19
  19. package/obj/template/docs/init/seed.md +152 -7
  20. package/package.json +1 -1
  21. package/src/.claude/workflows.template.jsonl +6 -0
  22. package/src/CLAUDE.template.md +14 -19
  23. package/src/cli/diff-render.js +54 -0
  24. package/src/cli/install.js +38 -3
  25. package/src/cli/manifest.js +7 -3
  26. package/src/cli/merge.js +107 -13
  27. package/src/cli/track-tasklist-materializer.js +223 -0
  28. package/src/cli/tui/upgrade.js +130 -27
  29. package/src/cli/upgrade-tiers.js +256 -0
  30. package/src/cli/workflow-migrator.js +40 -0
  31. package/src/cli/workflows-validator-invariants.js +417 -0
  32. package/src/cli/workflows-validator-predicates.js +19 -0
  33. package/src/cli/workflows-validator.js +156 -0
  34. package/src/seed.template.md +152 -7
  35. package/obj/template/.claude/skills/google-analytics/SKILL.md +0 -129
  36. package/obj/template/.claude/skills/google-analytics/references/audiences.md +0 -389
  37. package/obj/template/.claude/skills/google-analytics/references/bigquery.md +0 -470
  38. package/obj/template/.claude/skills/google-analytics/references/custom-dimensions.md +0 -355
  39. package/obj/template/.claude/skills/google-analytics/references/custom-events.md +0 -383
  40. package/obj/template/.claude/skills/google-analytics/references/data-management.md +0 -416
  41. package/obj/template/.claude/skills/google-analytics/references/debugview.md +0 -364
  42. package/obj/template/.claude/skills/google-analytics/references/events-fundamentals.md +0 -398
  43. package/obj/template/.claude/skills/google-analytics/references/gtag.md +0 -502
  44. package/obj/template/.claude/skills/google-analytics/references/gtm-integration.md +0 -483
  45. package/obj/template/.claude/skills/google-analytics/references/measurement-protocol.md +0 -519
  46. package/obj/template/.claude/skills/google-analytics/references/privacy.md +0 -441
  47. package/obj/template/.claude/skills/google-analytics/references/recommended-events.md +0 -464
  48. package/obj/template/.claude/skills/google-analytics/references/reporting.md +0 -397
  49. package/obj/template/.claude/skills/google-analytics/references/setup.md +0 -344
  50. package/obj/template/.claude/skills/google-analytics/references/user-tracking.md +0 -417
  51. package/obj/template/.claude/skills/optimize-seo/SKILL.md +0 -313
  52. package/obj/template/.claude/skills/optimize-seo/scripts/pagespeed.mjs +0 -197
  53. package/obj/template/.claude/skills/pagespeed-insights/LICENSE.md +0 -37
  54. package/obj/template/.claude/skills/pagespeed-insights/SKILL.md +0 -446
  55. package/obj/template/.claude/skills/pagespeed-insights/reference.md +0 -50
@@ -1,519 +0,0 @@
1
- # GA4 Measurement Protocol
2
-
3
- Complete guide to GA4 Measurement Protocol for server-side event tracking.
4
-
5
- ## Overview
6
-
7
- The GA4 Measurement Protocol allows server-side event collection, enabling data transmission to GA4 from any HTTP-capable environment including backend servers, mobile app backends, kiosks, and IoT devices.
8
-
9
- ## When to Use Measurement Protocol
10
-
11
- | Use Case | Description |
12
- |----------|-------------|
13
- | Server-side tracking | Events from backend systems |
14
- | Offline conversions | Import CRM/POS data |
15
- | Payment processing | Track post-checkout events |
16
- | Subscription renewals | Server-initiated events |
17
- | Lead enrichment | Enhance leads from backend |
18
- | IoT devices | Non-browser environments |
19
- | Headless architectures | API-first systems |
20
-
21
- ## API Endpoints
22
-
23
- ### Production Endpoint
24
-
25
- ```
26
- POST https://www.google-analytics.com/mp/collect
27
- ```
28
-
29
- ### Debug Endpoint
30
-
31
- ```
32
- POST https://www.google-analytics.com/debug/mp/collect
33
- ```
34
-
35
- **Key Difference:** Debug returns validation messages without storing data.
36
-
37
- ## Authentication
38
-
39
- ### Required Credentials
40
-
41
- 1. **Measurement ID** (G-XXXXXXXXXX)
42
- - Location: GA4 Admin -> Data Streams -> Web Stream
43
-
44
- 2. **API Secret**
45
- - Location: Data Streams -> Measurement Protocol API secrets
46
-
47
- ### Generating API Secret
48
-
49
- 1. GA4 Admin -> Data Streams
50
- 2. Click your data stream
51
- 3. Scroll to "Measurement Protocol API secrets"
52
- 4. Click "Create"
53
- 5. Enter nickname (e.g., "Server-side tracking")
54
- 6. Click "Create"
55
- 7. **Copy secret immediately** (shown once only)
56
- 8. Store securely
57
-
58
- ## Request Structure
59
-
60
- ### URL Format
61
-
62
- ```
63
- https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}
64
- ```
65
-
66
- ### Headers
67
-
68
- ```
69
- Content-Type: application/json
70
- ```
71
-
72
- ### Request Body (JSON)
73
-
74
- ```json
75
- {
76
- "client_id": "unique_client_identifier",
77
- "user_id": "optional_user_id",
78
- "timestamp_micros": "1234567890123456",
79
- "user_properties": {
80
- "property_name": {
81
- "value": "property_value"
82
- }
83
- },
84
- "consent": {
85
- "ad_storage": "granted",
86
- "analytics_storage": "granted"
87
- },
88
- "events": [
89
- {
90
- "name": "event_name",
91
- "params": {
92
- "parameter_name": "parameter_value",
93
- "value": 123.45,
94
- "currency": "USD"
95
- }
96
- }
97
- ]
98
- }
99
- ```
100
-
101
- ## Required and Optional Fields
102
-
103
- ### Required Fields
104
-
105
- | Field | Type | Description |
106
- |-------|------|-------------|
107
- | client_id | string | Unique client identifier (UUID recommended) |
108
- | events | array | Array of event objects (max 25) |
109
- | events[].name | string | Event name (max 40 chars) |
110
-
111
- ### Optional Fields
112
-
113
- | Field | Type | Description |
114
- |-------|------|-------------|
115
- | user_id | string | User ID for cross-device |
116
- | timestamp_micros | integer | Event timestamp (microseconds) |
117
- | user_properties | object | User-level properties |
118
- | consent | object | Consent status |
119
- | non_personalized_ads | boolean | Disable ad personalisation |
120
-
121
- ## Common Event Parameters
122
-
123
- | Parameter | Type | Description |
124
- |-----------|------|-------------|
125
- | session_id | string | Session identifier |
126
- | engagement_time_msec | integer | Engagement time (ms) |
127
- | page_location | string | Full URL |
128
- | page_title | string | Page title |
129
- | value | number | Monetary value |
130
- | currency | string | ISO 4217 code |
131
- | transaction_id | string | Unique transaction ID |
132
- | items | array | E-commerce items |
133
-
134
- ## Implementation Examples
135
-
136
- ### Python Implementation
137
-
138
- **Using Requests Library:**
139
-
140
- ```python
141
- import requests
142
- import json
143
- import uuid
144
-
145
- MEASUREMENT_ID = "G-XXXXXXXXXX"
146
- API_SECRET = "your_api_secret"
147
- ENDPOINT = f"https://www.google-analytics.com/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"
148
-
149
- def send_event(client_id, event_name, params=None):
150
- payload = {
151
- "client_id": client_id,
152
- "events": [{
153
- "name": event_name,
154
- "params": params or {}
155
- }]
156
- }
157
-
158
- response = requests.post(
159
- ENDPOINT,
160
- headers={"Content-Type": "application/json"},
161
- data=json.dumps(payload)
162
- )
163
-
164
- return response.status_code == 204
165
-
166
- # Send page view
167
- send_event(
168
- client_id="user_123.session_456",
169
- event_name="page_view",
170
- params={
171
- "page_location": "https://example.com/page",
172
- "page_title": "Example Page"
173
- }
174
- )
175
-
176
- # Send purchase
177
- send_event(
178
- client_id="user_123.session_456",
179
- event_name="purchase",
180
- params={
181
- "transaction_id": "T_12345",
182
- "value": 99.99,
183
- "currency": "USD",
184
- "items": [{
185
- "item_id": "SKU_123",
186
- "item_name": "Product Name",
187
- "price": 99.99,
188
- "quantity": 1
189
- }]
190
- }
191
- )
192
- ```
193
-
194
- **Using ga4mp Library:**
195
-
196
- ```python
197
- # Install: pip install ga4mp
198
- from ga4mp import GtagMP
199
-
200
- ga = GtagMP(
201
- measurement_id="G-XXXXXXXXXX",
202
- api_secret="your_api_secret",
203
- client_id="unique_client_id"
204
- )
205
-
206
- # Send event
207
- ga.send_event(
208
- event_name="purchase",
209
- event_parameters={
210
- "transaction_id": "T_12345",
211
- "value": 99.99,
212
- "currency": "USD",
213
- "items": [{
214
- "item_id": "SKU_123",
215
- "item_name": "Product Name",
216
- "price": 99.99,
217
- "quantity": 1
218
- }]
219
- }
220
- )
221
- ```
222
-
223
- ### Node.js Implementation
224
-
225
- ```javascript
226
- const axios = require('axios');
227
- const { v4: uuidv4 } = require('uuid');
228
-
229
- const MEASUREMENT_ID = 'G-XXXXXXXXXX';
230
- const API_SECRET = 'your_api_secret';
231
- const ENDPOINT = `https://www.google-analytics.com/mp/collect?measurement_id=${MEASUREMENT_ID}&api_secret=${API_SECRET}`;
232
-
233
- async function sendEvent(clientId, eventName, params = {}) {
234
- const payload = {
235
- client_id: clientId,
236
- events: [{
237
- name: eventName,
238
- params: params
239
- }]
240
- };
241
-
242
- try {
243
- const response = await axios.post(ENDPOINT, payload, {
244
- headers: { 'Content-Type': 'application/json' }
245
- });
246
- return response.status === 204;
247
- } catch (error) {
248
- console.error('Error sending event:', error);
249
- return false;
250
- }
251
- }
252
-
253
- // Send purchase event
254
- sendEvent('client_123', 'purchase', {
255
- transaction_id: 'T_12345',
256
- value: 99.99,
257
- currency: 'USD',
258
- items: [{
259
- item_id: 'SKU_123',
260
- item_name: 'Product',
261
- price: 99.99,
262
- quantity: 1
263
- }]
264
- });
265
- ```
266
-
267
- ### PHP Implementation
268
-
269
- ```php
270
- <?php
271
- // Using php-GA4-Measurement-Protocol library
272
- // Install: composer require br33f/php-ga4-measurement-protocol
273
-
274
- use Br33f\Ga4\MeasurementProtocol\Dto\Event\PurchaseEvent;
275
- use Br33f\Ga4\MeasurementProtocol\Dto\Request\MeasurementRequest;
276
- use Br33f\Ga4\MeasurementProtocol\Service;
277
-
278
- $measurementId = 'G-XXXXXXXXXX';
279
- $apiSecret = 'your_api_secret';
280
-
281
- $service = new Service($apiSecret, $measurementId);
282
-
283
- $event = new PurchaseEvent();
284
- $event->setTransactionId('T_12345')
285
- ->setValue(99.99)
286
- ->setCurrency('USD');
287
-
288
- $request = new MeasurementRequest();
289
- $request->setClientId('unique_client_id')
290
- ->addEvent($event);
291
-
292
- $service->send($request);
293
- ?>
294
- ```
295
-
296
- ### cURL Example
297
-
298
- ```bash
299
- curl -X POST "https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_SECRET" \
300
- -H "Content-Type: application/json" \
301
- -d '{
302
- "client_id": "client_123",
303
- "events": [{
304
- "name": "purchase",
305
- "params": {
306
- "transaction_id": "T_12345",
307
- "value": 99.99,
308
- "currency": "USD"
309
- }
310
- }]
311
- }'
312
- ```
313
-
314
- ## Validation with Debug Endpoint
315
-
316
- ### Send to Debug Endpoint
317
-
318
- ```bash
319
- curl -X POST "https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_SECRET" \
320
- -H "Content-Type: application/json" \
321
- -d '{
322
- "client_id": "test_client",
323
- "events": [{
324
- "name": "test_event",
325
- "params": {
326
- "test_param": "test_value"
327
- }
328
- }]
329
- }'
330
- ```
331
-
332
- ### Response Format
333
-
334
- ```json
335
- {
336
- "validationMessages": [
337
- {
338
- "fieldPath": "events[0].name",
339
- "description": "Event name must be 40 characters or fewer",
340
- "validationCode": "NAME_INVALID"
341
- }
342
- ]
343
- }
344
- ```
345
-
346
- ### Empty Response = Valid
347
-
348
- - No validationMessages = payload valid
349
- - Status 200 = request processed
350
- - Production returns 204 (no content)
351
-
352
- ## Validation Codes
353
-
354
- | Code | Description | Fix |
355
- |------|-------------|-----|
356
- | NAME_INVALID | Invalid event/parameter name | snake_case, max 40 chars |
357
- | NAME_RESERVED | Reserved name used | Check reserved names |
358
- | VALUE_INVALID | Invalid parameter value | Check data type |
359
- | VALUE_REQUIRED | Required value missing | Add required parameter |
360
- | VALUE_OUT_OF_BOUNDS | Value exceeds limits | Check numeric ranges |
361
- | EXCEEDED_MAX_ENTITIES | Too many events | Max 25 per request |
362
-
363
- ## Best Practices
364
-
365
- ### 1. Always Validate First
366
-
367
- ```python
368
- DEBUG_ENDPOINT = f"https://www.google-analytics.com/debug/mp/collect?measurement_id={MEASUREMENT_ID}&api_secret={API_SECRET}"
369
-
370
- def validate_event(payload):
371
- response = requests.post(
372
- DEBUG_ENDPOINT,
373
- headers={"Content-Type": "application/json"},
374
- data=json.dumps(payload)
375
- )
376
- return response.json()
377
- ```
378
-
379
- ### 2. Use Consistent client_id
380
-
381
- - Same user = same client_id across sessions
382
- - Store in database for logged-in users
383
- - Use UUID format for anonymity
384
-
385
- ### 3. Include session_id
386
-
387
- - Maintain session continuity
388
- - Generate unique session ID
389
- - Keep consistent within session
390
-
391
- ### 4. Batch Events
392
-
393
- ```python
394
- payload = {
395
- "client_id": "client_123",
396
- "events": [
397
- {"name": "event_1", "params": {}},
398
- {"name": "event_2", "params": {}},
399
- {"name": "event_3", "params": {}}
400
- ] # Up to 25 events per request
401
- }
402
- ```
403
-
404
- ### 5. Handle Errors Gracefully
405
-
406
- ```python
407
- def send_with_retry(payload, max_retries=3):
408
- for attempt in range(max_retries):
409
- try:
410
- response = requests.post(ENDPOINT, json=payload)
411
- if response.status_code == 204:
412
- return True
413
- except Exception as e:
414
- if attempt < max_retries - 1:
415
- time.sleep(2 ** attempt) # Exponential backoff
416
- else:
417
- log_error(e, payload)
418
- return False
419
- ```
420
-
421
- ### 6. Set Proper Timestamps
422
-
423
- ```python
424
- import time
425
-
426
- # For historical data (max 3 days past)
427
- timestamp_micros = int(time.time() * 1_000_000)
428
-
429
- payload = {
430
- "client_id": "client_123",
431
- "timestamp_micros": str(timestamp_micros),
432
- "events": [...]
433
- }
434
- ```
435
-
436
- ### 7. Respect Consent
437
-
438
- ```python
439
- payload = {
440
- "client_id": "client_123",
441
- "consent": {
442
- "ad_storage": "denied",
443
- "analytics_storage": "granted"
444
- },
445
- "events": [...]
446
- }
447
- ```
448
-
449
- ## Limits
450
-
451
- | Limit | Value |
452
- |-------|-------|
453
- | Events per request | 25 |
454
- | Event name length | 40 characters |
455
- | Parameter name length | 40 characters |
456
- | Parameter value length | 100 characters |
457
- | Parameters per event | 25 |
458
- | User properties per request | 25 |
459
- | Timestamp range | 3 days past, 72 hours future |
460
-
461
- ## Common Issues
462
-
463
- ### Events Not Appearing
464
-
465
- **Causes:**
466
- - Wrong Measurement ID
467
- - Invalid API secret
468
- - Validation errors
469
- - Consent blocking
470
-
471
- **Solutions:**
472
- 1. Validate with debug endpoint
473
- 2. Verify credentials
474
- 3. Check consent parameters
475
-
476
- ### Duplicate Events
477
-
478
- **Causes:**
479
- - Same request sent multiple times
480
- - Missing deduplication logic
481
-
482
- **Solutions:**
483
- 1. Implement idempotency
484
- 2. Track sent events
485
- 3. Use unique transaction IDs
486
-
487
- ### Missing User Association
488
-
489
- **Cause:** Different client_id values
490
-
491
- **Solution:** Store and reuse client_id from frontend
492
-
493
- ## Quick Reference
494
-
495
- ### Endpoint
496
-
497
- ```
498
- Production: /mp/collect
499
- Debug: /debug/mp/collect
500
- ```
501
-
502
- ### Required Fields
503
-
504
- - client_id (UUID recommended)
505
- - events array
506
- - event name
507
-
508
- ### Max Limits
509
-
510
- - 25 events per request
511
- - 40 characters per event name
512
- - 25 parameters per event
513
- - 25 user properties per request
514
-
515
- ### Validation
516
-
517
- - Send to debug endpoint
518
- - Empty response = valid
519
- - Check validationMessages array