@contentrain/skills 0.1.2 → 0.2.1
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/README.md +65 -60
- package/dist/index.cjs +65 -1
- package/dist/index.d.cts +50 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +50 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +65 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
- package/skills/contentrain/SKILL.md +149 -0
- package/skills/contentrain/references/architecture.md +212 -0
- package/skills/contentrain/references/content-formats.md +279 -0
- package/skills/contentrain/references/i18n.md +298 -0
- package/skills/contentrain/references/mcp-pipelines.md +195 -0
- package/skills/contentrain/references/mcp-tools.md +308 -0
- package/skills/contentrain/references/model-kinds.md +330 -0
- package/skills/contentrain/references/schema-types.md +177 -0
- package/skills/contentrain/references/security.md +146 -0
- package/skills/contentrain/references/workflow.md +285 -0
- package/skills/contentrain-bulk/SKILL.md +99 -0
- package/skills/contentrain-content/SKILL.md +162 -0
- package/skills/contentrain-diff/SKILL.md +62 -0
- package/skills/contentrain-doctor/SKILL.md +62 -0
- package/skills/contentrain-generate/SKILL.md +183 -0
- package/skills/contentrain-generate/references/generated-client.md +198 -0
- package/skills/contentrain-init/SKILL.md +99 -0
- package/skills/contentrain-model/SKILL.md +141 -0
- package/skills/contentrain-normalize/SKILL.md +185 -0
- package/skills/contentrain-normalize/references/extraction.md +164 -0
- package/skills/contentrain-normalize/references/reuse.md +146 -0
- package/skills/contentrain-normalize/references/what-is-content.md +115 -0
- package/skills/contentrain-quality/SKILL.md +180 -0
- package/skills/contentrain-quality/references/accessibility.md +160 -0
- package/skills/contentrain-quality/references/content-quality.md +299 -0
- package/skills/contentrain-quality/references/media.md +170 -0
- package/skills/contentrain-quality/references/seo.md +229 -0
- package/skills/contentrain-review/SKILL.md +170 -0
- package/skills/contentrain-sdk/SKILL.md +145 -0
- package/skills/contentrain-sdk/references/bundler-config.md +135 -0
- package/skills/contentrain-serve/SKILL.md +96 -0
- package/skills/contentrain-translate/SKILL.md +180 -0
- package/skills/contentrain-validate-fix/SKILL.md +92 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: schema-types
|
|
3
|
+
description: "Complete 27-type catalog organized by family with field properties, validation rules, and examples."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Schema Types Reference
|
|
7
|
+
|
|
8
|
+
Contentrain uses a **flat type system** with 27 types. Each type is a single keyword -- no `format` sub-layer. `type: "email"` is the complete specification.
|
|
9
|
+
|
|
10
|
+
## String Family (11 types)
|
|
11
|
+
|
|
12
|
+
| Type | Description | Validation | JSON Schema Export |
|
|
13
|
+
|------|-------------|------------|-------------------|
|
|
14
|
+
| `string` | Single-line text | -- | `{ "type": "string" }` |
|
|
15
|
+
| `text` | Multi-line text | -- | `{ "type": "string" }` |
|
|
16
|
+
| `email` | Email address | RFC 5321 | `{ "type": "string", "format": "email" }` |
|
|
17
|
+
| `url` | URL | RFC 3986 | `{ "type": "string", "format": "uri" }` |
|
|
18
|
+
| `slug` | URL-safe identifier | `/^[a-z0-9]+(?:-[a-z0-9]+)*$/` | `{ "type": "string", "pattern": "..." }` |
|
|
19
|
+
| `color` | Hex color code | `/^#[0-9a-fA-F]{6}$/` | `{ "type": "string" }` |
|
|
20
|
+
| `phone` | Phone number | E.164 or freeform | `{ "type": "string" }` |
|
|
21
|
+
| `code` | Code snippet | -- | `{ "type": "string" }` |
|
|
22
|
+
| `icon` | Icon identifier | -- | `{ "type": "string" }` |
|
|
23
|
+
| `markdown` | Markdown content | -- | `{ "type": "string" }` |
|
|
24
|
+
| `richtext` | HTML rich text | -- | `{ "type": "string" }` |
|
|
25
|
+
|
|
26
|
+
### String Family Examples
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
"title": { "type": "string", "required": true, "min": 3, "max": 120 }
|
|
30
|
+
"bio": { "type": "text", "max": 500 }
|
|
31
|
+
"contact_email": { "type": "email", "required": true, "unique": true }
|
|
32
|
+
"website": { "type": "url" }
|
|
33
|
+
"handle": { "type": "slug", "required": true, "unique": true }
|
|
34
|
+
"brand_color": { "type": "color", "default": "#4F46E5" }
|
|
35
|
+
"mobile": { "type": "phone" }
|
|
36
|
+
"snippet": { "type": "code" }
|
|
37
|
+
"logo_icon": { "type": "icon" }
|
|
38
|
+
"body": { "type": "markdown", "required": true }
|
|
39
|
+
"description": { "type": "richtext" }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Number Family (5 types)
|
|
43
|
+
|
|
44
|
+
| Type | Description | Constraints | JSON Schema Export |
|
|
45
|
+
|------|-------------|-------------|-------------------|
|
|
46
|
+
| `number` | General number | -- | `{ "type": "number" }` |
|
|
47
|
+
| `integer` | Whole number | -- | `{ "type": "integer" }` |
|
|
48
|
+
| `decimal` | Decimal number | -- | `{ "type": "number" }` |
|
|
49
|
+
| `percent` | Percentage | 0-100 | `{ "type": "number", "minimum": 0, "maximum": 100 }` |
|
|
50
|
+
| `rating` | Rating score | 1-5 | `{ "type": "integer", "minimum": 1, "maximum": 5 }` |
|
|
51
|
+
|
|
52
|
+
### Number Family Examples
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
"price": { "type": "decimal", "required": true, "min": 0 }
|
|
56
|
+
"quantity": { "type": "integer", "min": 0, "max": 10000 }
|
|
57
|
+
"discount": { "type": "percent" }
|
|
58
|
+
"score": { "type": "rating", "required": true }
|
|
59
|
+
"weight": { "type": "number" }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Primitives (3 types)
|
|
63
|
+
|
|
64
|
+
| Type | Storage Format | JSON Schema Export |
|
|
65
|
+
|------|---------------|-------------------|
|
|
66
|
+
| `boolean` | `true` / `false` | `{ "type": "boolean" }` |
|
|
67
|
+
| `date` | `"YYYY-MM-DD"` string | `{ "type": "string", "format": "date" }` |
|
|
68
|
+
| `datetime` | ISO 8601 string | `{ "type": "string", "format": "date-time" }` |
|
|
69
|
+
|
|
70
|
+
### Primitives Examples
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
"is_featured": { "type": "boolean", "default": false }
|
|
74
|
+
"publish_date": { "type": "date", "required": true }
|
|
75
|
+
"event_start": { "type": "datetime", "required": true }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Media (3 types)
|
|
79
|
+
|
|
80
|
+
| Type | Storage | Description |
|
|
81
|
+
|------|---------|-------------|
|
|
82
|
+
| `image` | Relative path (string) | Image file reference |
|
|
83
|
+
| `video` | Relative path (string) | Video file reference |
|
|
84
|
+
| `file` | Relative path (string) | Generic file reference |
|
|
85
|
+
|
|
86
|
+
In v1, media fields store URL/path strings only. Upload and processing are out of scope.
|
|
87
|
+
|
|
88
|
+
### Media Examples
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
"avatar": { "type": "image", "accept": "image/png,image/jpeg,image/webp", "maxSize": 2097152 }
|
|
92
|
+
"intro_video": { "type": "video", "accept": "video/mp4,video/webm" }
|
|
93
|
+
"resume": { "type": "file", "accept": "application/pdf" }
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Relations (2 types)
|
|
97
|
+
|
|
98
|
+
| Type | Cardinality | Storage |
|
|
99
|
+
|------|-------------|---------|
|
|
100
|
+
| `relation` | One-to-one | `"entry-id"` (string) |
|
|
101
|
+
| `relations` | One-to-many | `["id-1", "id-2"]` (string array) |
|
|
102
|
+
|
|
103
|
+
### Relation Examples
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
"author": { "type": "relation", "model": "team-members", "required": true }
|
|
107
|
+
"categories": { "type": "relations", "model": "categories" }
|
|
108
|
+
"related_content": { "type": "relation", "model": ["blog-post", "page"] }
|
|
109
|
+
"parent": { "type": "relation", "model": "categories" }
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Structural (3 types)
|
|
113
|
+
|
|
114
|
+
| Type | Description | Requires |
|
|
115
|
+
|------|-------------|----------|
|
|
116
|
+
| `select` | Fixed options, pick one | `options` property |
|
|
117
|
+
| `array` | Ordered list of items | `items` property |
|
|
118
|
+
| `object` | Nested key-value structure | `fields` property |
|
|
119
|
+
|
|
120
|
+
### Structural Examples
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
"status": { "type": "select", "options": ["draft", "published", "archived"], "default": "draft" }
|
|
124
|
+
|
|
125
|
+
"tags": { "type": "array", "items": "string", "min": 1, "max": 10 }
|
|
126
|
+
|
|
127
|
+
"variants": {
|
|
128
|
+
"type": "array",
|
|
129
|
+
"items": {
|
|
130
|
+
"type": "object",
|
|
131
|
+
"fields": {
|
|
132
|
+
"color": { "type": "color", "required": true },
|
|
133
|
+
"price": { "type": "decimal", "required": true },
|
|
134
|
+
"size": { "type": "select", "options": ["S", "M", "L"] }
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"max": 50
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
"address": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"fields": {
|
|
143
|
+
"city": { "type": "string", "required": true },
|
|
144
|
+
"street": { "type": "string", "required": true },
|
|
145
|
+
"zip": { "type": "string" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Field Properties Reference
|
|
151
|
+
|
|
152
|
+
| Property | Applicable Types | Description |
|
|
153
|
+
|----------|-----------------|-------------|
|
|
154
|
+
| `type` | ALL | **Required.** One of the 27 types |
|
|
155
|
+
| `required` | ALL | Mark field as mandatory. Default: `false`. Omit if `false` |
|
|
156
|
+
| `unique` | `string`, `email`, `slug`, `integer` | Enforce uniqueness within model. Default: `false`. Omit if `false` |
|
|
157
|
+
| `default` | ALL | Default value. Omit if `null` |
|
|
158
|
+
| `min` | string/text: char count; numbers: value; array: element count | Minimum constraint |
|
|
159
|
+
| `max` | Same as `min` | Maximum constraint |
|
|
160
|
+
| `pattern` | `string`, `text`, `code` | Regex validation pattern |
|
|
161
|
+
| `options` | `select` ONLY | Fixed choices: `["draft", "published", "archived"]` |
|
|
162
|
+
| `model` | `relation`, `relations` ONLY | Target model ID. String or string array for polymorphic |
|
|
163
|
+
| `items` | `array` ONLY | Element type: `"string"` or `{ "type": "object", "fields": {...} }` |
|
|
164
|
+
| `fields` | `object` ONLY | Nested field definitions |
|
|
165
|
+
| `accept` | `image`, `video`, `file` | Allowed MIME types: `"image/png,image/jpeg"` |
|
|
166
|
+
| `maxSize` | `image`, `video`, `file` | Maximum file size in bytes |
|
|
167
|
+
| `description` | ALL | Human-readable hint (shown in Studio UI tooltip, used as agent context) |
|
|
168
|
+
|
|
169
|
+
## Omission Rules
|
|
170
|
+
|
|
171
|
+
These rules produce minimal, clean schema definitions:
|
|
172
|
+
|
|
173
|
+
- If `required` is `false` (the default), do NOT include it
|
|
174
|
+
- If `unique` is `false` (the default), do NOT include it
|
|
175
|
+
- If `default` is `null`, do NOT include it
|
|
176
|
+
- Only include properties that add information
|
|
177
|
+
- Fewer properties = fewer tokens = faster agent processing
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security
|
|
3
|
+
description: "Security rules: XSS prevention, secret detection, PII handling, URL validation"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Security Rules
|
|
7
|
+
|
|
8
|
+
These rules are MANDATORY for all AI agents creating or editing content in Contentrain projects.
|
|
9
|
+
Content that violates these rules MUST be rejected. These rules protect against XSS, data leaks, and injection attacks.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## XSS Prevention in Content
|
|
14
|
+
|
|
15
|
+
1. `richtext` and `markdown` fields MUST NOT contain any of the following:
|
|
16
|
+
- `<script>` or `</script>` tags
|
|
17
|
+
- `javascript:` protocol in any attribute value
|
|
18
|
+
- Event handler attributes: `onerror`, `onload`, `onclick`, `onmouseover`, `onfocus`, `onblur`, `onsubmit`, `onchange`, `onkeydown`, `onkeyup`, `onkeypress`, `ondblclick`, `onmousedown`, `onmouseup`, `onmousemove`, `onmouseout`, `oncontextmenu`, `ondrag`, `ondrop`, `onpaste`, `oninput`
|
|
19
|
+
- `<iframe>`, `<embed>`, `<object>`, `<applet>`, `<form>`, `<input>`, `<textarea>`, `<select>`, `<button>` tags
|
|
20
|
+
- `data:` URIs containing executable content (e.g., `data:text/html`, `data:application/javascript`)
|
|
21
|
+
- CSS expressions: `expression()`, `url(javascript:)`, `-moz-binding`
|
|
22
|
+
- `<base>` tag (can redirect all relative URLs)
|
|
23
|
+
- `<meta>` tag with `http-equiv="refresh"`
|
|
24
|
+
- `<svg>` with embedded scripts or event handlers
|
|
25
|
+
|
|
26
|
+
2. **Allowed HTML tags** in richtext fields:
|
|
27
|
+
```
|
|
28
|
+
p, strong, em, a, ul, ol, li, h2, h3, h4, h5, h6,
|
|
29
|
+
blockquote, code, pre, img, br, hr,
|
|
30
|
+
table, thead, tbody, tr, th, td,
|
|
31
|
+
del, ins, sup, sub, abbr, mark, details, summary
|
|
32
|
+
```
|
|
33
|
+
Any tag not on this list MUST be stripped or rejected.
|
|
34
|
+
|
|
35
|
+
3. **Allowed attributes on `<a>` tags**: `href`, `title`, `target`.
|
|
36
|
+
- `href` MUST use `http://`, `https://`, or `mailto:` protocol only.
|
|
37
|
+
- `target` MUST be `_blank` or `_self` only.
|
|
38
|
+
- When `target="_blank"`, the frontend should add `rel="noopener noreferrer"` — but the content itself must not rely on this.
|
|
39
|
+
|
|
40
|
+
4. **Allowed attributes on `<img>` tags**: `src`, `alt`, `width`, `height`.
|
|
41
|
+
- `src` MUST use `https://`, `http://`, or a relative path starting with `/` or `./`.
|
|
42
|
+
- No `data:` URIs for `src` in production content.
|
|
43
|
+
|
|
44
|
+
5. Strip or reject all other HTML attributes not explicitly allowed above.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Secret Detection
|
|
49
|
+
|
|
50
|
+
1. NEVER include any of the following in ANY content field:
|
|
51
|
+
- **API keys**: patterns matching `sk-`, `pk_`, `sk_live_`, `sk_test_`, `api_key=`, `apiKey:`, `x-api-key`
|
|
52
|
+
- **Access tokens**: JWT tokens (`eyJ...`), Bearer tokens, OAuth tokens, session tokens
|
|
53
|
+
- **Passwords or credentials**: any field value resembling `password=`, `passwd:`, `secret=`
|
|
54
|
+
- **Private keys**: `-----BEGIN RSA PRIVATE KEY-----`, `-----BEGIN OPENSSH PRIVATE KEY-----`, `-----BEGIN PGP PRIVATE KEY BLOCK-----`, or similar PEM blocks
|
|
55
|
+
- **Database connection strings**: `mongodb://`, `postgres://`, `mysql://`, `redis://` with credentials
|
|
56
|
+
- **Cloud credentials**: AWS (`AKIA...`), GCP (`AIza...`), Azure storage keys
|
|
57
|
+
- **Webhook URLs with tokens**: URLs containing `token=`, `secret=`, or `/hooks/` with embedded credentials
|
|
58
|
+
|
|
59
|
+
2. If a field value matches any secret pattern, **reject the value** and warn the user: `"This value appears to contain a secret or credential. Secrets must not be stored in content fields."`
|
|
60
|
+
|
|
61
|
+
3. Environment-specific values (API endpoints, service IDs) SHOULD use placeholder references (e.g., `{{API_URL}}`) or be stored in environment configuration, not hardcoded in content.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## PII Handling
|
|
66
|
+
|
|
67
|
+
1. **Flag** when email addresses appear in fields that are not typed as `email`. The agent should warn: `"Email address detected in a non-email field. Confirm this is intentional."`
|
|
68
|
+
|
|
69
|
+
2. **Flag** when phone numbers appear in fields that are not typed as `phone`.
|
|
70
|
+
|
|
71
|
+
3. **Flag** physical addresses, government ID numbers (SSN, national ID), and dates of birth when they appear in generic `string` or `richtext` fields.
|
|
72
|
+
|
|
73
|
+
4. User-generated content fields SHOULD be marked for frontend sanitization in the model metadata.
|
|
74
|
+
|
|
75
|
+
5. NEVER store authentication credentials (usernames, passwords, tokens) in content fields.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## URL Validation
|
|
80
|
+
|
|
81
|
+
1. All URLs in `url` type fields MUST use `https://` protocol.
|
|
82
|
+
- **Exception**: `http://localhost` and `http://127.0.0.1` URLs are allowed for development references.
|
|
83
|
+
|
|
84
|
+
2. Reject URLs containing path traversal sequences: `../`, `..\\`, `%2e%2e/`, `%2e%2e%5c`.
|
|
85
|
+
|
|
86
|
+
3. URLs MUST have a valid format: protocol + domain + valid TLD. Reject malformed URLs.
|
|
87
|
+
|
|
88
|
+
4. Do not use IP-based URLs in production content. Use domain names.
|
|
89
|
+
- **Exception**: private/internal documentation that explicitly references infrastructure IPs.
|
|
90
|
+
|
|
91
|
+
5. Reject `file://` protocol URLs. Content must never reference local filesystem paths as URLs.
|
|
92
|
+
|
|
93
|
+
6. Reject `ftp://` protocol URLs unless the content model explicitly documents FTP support.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## File & Media Security
|
|
98
|
+
|
|
99
|
+
1. Media paths MUST be relative to the `assets_path` defined in `config.json`. Never use absolute filesystem paths.
|
|
100
|
+
|
|
101
|
+
2. Reject any file path containing:
|
|
102
|
+
- Absolute system paths: `/etc/`, `/usr/`, `/var/`, `C:\\`, `C:/`, `/Users/`, `/home/`
|
|
103
|
+
- Path traversal: `../`, `..\\`, `%2e%2e`
|
|
104
|
+
|
|
105
|
+
3. Validate file extensions match the field type:
|
|
106
|
+
- `image` fields: `.webp`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.avif`
|
|
107
|
+
- `video` fields: `.mp4`, `.webm`, `.ogg`
|
|
108
|
+
- `file` fields: any extension, but **flag executables** and warn the user: `.exe`, `.sh`, `.bat`, `.cmd`, `.ps1`, `.msi`, `.dmg`, `.app`, `.jar`, `.dll`, `.so`
|
|
109
|
+
|
|
110
|
+
4. File names MUST NOT contain spaces or special characters. Only alphanumeric characters, hyphens (`-`), underscores (`_`), and dots (`.`) are allowed.
|
|
111
|
+
|
|
112
|
+
5. Reject files with double extensions that disguise executables: `report.pdf.exe`, `image.jpg.sh`.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Content Injection
|
|
117
|
+
|
|
118
|
+
1. Values in `object` and `array` type fields MUST be valid JSON. Reject values with:
|
|
119
|
+
- Trailing commas
|
|
120
|
+
- JavaScript comments (`//`, `/* */`)
|
|
121
|
+
- Unquoted keys
|
|
122
|
+
- Single-quoted strings
|
|
123
|
+
- Executable code patterns (function declarations, `eval()`, `require()`, `import()`)
|
|
124
|
+
|
|
125
|
+
2. `code` type fields are displayed as code and never executed, but still apply these rules:
|
|
126
|
+
- No embedded secrets (see Secret Detection above)
|
|
127
|
+
- Content is stored as-is — no sanitization needed beyond secret detection
|
|
128
|
+
|
|
129
|
+
3. Template literals and interpolation patterns (`${...}`, `{{...}}`, `{%...%}`) in content fields MUST be intentional. Flag unexpected template syntax in plain text fields.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Markdown-Specific
|
|
134
|
+
|
|
135
|
+
1. Fenced code blocks (`` ``` ``) are rendered as code, not executed. They are safe from an execution standpoint but still subject to secret detection rules.
|
|
136
|
+
|
|
137
|
+
2. Raw HTML embedded in markdown MUST follow the same XSS rules as richtext fields. Apply the allowed-tags whitelist.
|
|
138
|
+
|
|
139
|
+
3. Link destinations in markdown (`[text](url)`) MUST use `http://`, `https://`, or `mailto:` protocol only. Reject `javascript:`, `data:`, `vbscript:`, and `file:` protocols.
|
|
140
|
+
|
|
141
|
+
4. Image sources in markdown (``) MUST use valid paths:
|
|
142
|
+
- No path traversal (`../`)
|
|
143
|
+
- Valid image extensions (`.webp`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`, `.avif`)
|
|
144
|
+
- `https://` for external images, relative paths for local images
|
|
145
|
+
|
|
146
|
+
5. Do not allow HTML comments (`<!-- -->`) to hide content in markdown — they may be rendered in some parsers and can be used to smuggle payloads.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow
|
|
3
|
+
description: "Git workflow rules: branching, validation, review, conflict resolution, status management"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contentrain Workflow Rules
|
|
7
|
+
|
|
8
|
+
> These rules govern how content changes flow through the Git-based workflow, including branching, validation, review, and status management.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Two Workflow Modes
|
|
13
|
+
|
|
14
|
+
Contentrain supports two workflow modes, configured in `.contentrain/config.json` under the `workflow` field.
|
|
15
|
+
|
|
16
|
+
### 1.1 auto-merge
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{ "workflow": "auto-merge" }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Feature branch is created from the `contentrain` branch, changes are committed, feature branch is **merged into `contentrain`**, then baseBranch is advanced via **update-ref** (fast-forward), and `.contentrain/` files are **selectively synced** to the developer's working tree.
|
|
23
|
+
- No review step -- changes go live immediately.
|
|
24
|
+
- Best for: solo developers, rapid iteration, prototyping, vibe coding.
|
|
25
|
+
- Status flow: `draft` --> `published` (skips `in_review`).
|
|
26
|
+
|
|
27
|
+
### 1.2 review
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{ "workflow": "review" }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- Branch is created, changes are committed, branch is **pushed to remote**.
|
|
34
|
+
- Studio or team reviews the changes before merging.
|
|
35
|
+
- Best for: teams, content governance, production environments.
|
|
36
|
+
- Status flow: `draft` --> `in_review` --> `published` | `rejected`.
|
|
37
|
+
|
|
38
|
+
### 1.3 Mode Selection Rules
|
|
39
|
+
|
|
40
|
+
- Default is `auto-merge` -- minimal friction for getting started.
|
|
41
|
+
- Switch to `review` as the project or team grows and governance becomes important.
|
|
42
|
+
- **Normalize operations ALWAYS use `review` mode** regardless of the config setting. Normalize changes are never auto-merged.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. Branch Naming Convention
|
|
47
|
+
|
|
48
|
+
All Contentrain branches follow a strict naming pattern:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
contentrain/{operation}/{model}/{locale}/{timestamp}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Examples
|
|
55
|
+
|
|
56
|
+
| Scenario | Branch Name |
|
|
57
|
+
|----------|-------------|
|
|
58
|
+
| Content update | `contentrain/content/blog-post/en/1710300000` |
|
|
59
|
+
| Model creation | `contentrain/model/team-member/1710300000` |
|
|
60
|
+
| Normalize extraction | `contentrain/normalize/extract/blog/1710300000` |
|
|
61
|
+
| Normalize reuse | `contentrain/normalize/reuse/marketing-hero/en/1710300000` |
|
|
62
|
+
| Scaffold | `contentrain/new/scaffold-landing/en/1710300000` |
|
|
63
|
+
|
|
64
|
+
### Rules
|
|
65
|
+
|
|
66
|
+
- Branches are created automatically by MCP tools. Do NOT create them manually.
|
|
67
|
+
- The `{timestamp}` component ensures uniqueness.
|
|
68
|
+
- `{locale}` is included when the operation is locale-specific.
|
|
69
|
+
- `{model}` is included when the operation targets a specific model.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 3. Git Workflow
|
|
74
|
+
|
|
75
|
+
### 3.1 Dedicated `contentrain` Branch
|
|
76
|
+
|
|
77
|
+
The `contentrain` branch is the **single source of truth** for content state:
|
|
78
|
+
|
|
79
|
+
- Created automatically at `contentrain_init`.
|
|
80
|
+
- All content writes happen on feature branches forked from `contentrain`.
|
|
81
|
+
- The `contentrain` branch is **protected from deletion**.
|
|
82
|
+
- context.json is committed together with content changes, not as a separate commit.
|
|
83
|
+
|
|
84
|
+
### 3.2 Worktree-Based Transactions
|
|
85
|
+
|
|
86
|
+
Every write operation follows this flow:
|
|
87
|
+
|
|
88
|
+
1. MCP creates a **temporary Git worktree** on a new feature branch forked from `contentrain`.
|
|
89
|
+
2. Changes are made in the worktree (content files, model files, context.json).
|
|
90
|
+
3. Changes are committed to the feature branch.
|
|
91
|
+
4. **auto-merge mode:** Feature branch is merged into `contentrain`, then baseBranch is advanced via **update-ref** (fast-forward), then `.contentrain/` files are **selectively synced** to the developer's working tree. Dirty files are skipped with a warning. Worktree is cleaned up.
|
|
92
|
+
5. **review mode:** Feature branch is pushed to remote for team review. Worktree is cleaned up. Studio notifies reviewers.
|
|
93
|
+
|
|
94
|
+
Developer's working tree is **never mutated** during MCP git operations. There is no stash, no checkout, and no merge on the developer's tree.
|
|
95
|
+
|
|
96
|
+
### 3.3 Developer Manual Editing
|
|
97
|
+
|
|
98
|
+
If the developer manually edits `.contentrain/` files in their working tree, MCP selective sync skips those dirty files and issues a warning. The developer must commit or discard their local changes before MCP can sync those files.
|
|
99
|
+
|
|
100
|
+
### 3.4 Critical Rules
|
|
101
|
+
|
|
102
|
+
- **NEVER commit directly to main or the `contentrain` branch.** All changes go through feature branches.
|
|
103
|
+
- **NEVER create branches manually.** MCP tools handle all Git operations.
|
|
104
|
+
- **NEVER force-push or rebase** Contentrain branches.
|
|
105
|
+
- **NEVER delete the `contentrain` branch.** It is the content state SSOT.
|
|
106
|
+
- Worktrees are temporary. They are created for the operation and cleaned up afterward.
|
|
107
|
+
- Each branch contains a cohesive set of changes (e.g., all entries for one model update).
|
|
108
|
+
|
|
109
|
+
### 3.5 Branch Lifecycle
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Feature branch created (worktree on contentrain)
|
|
113
|
+
--> Changes committed
|
|
114
|
+
--> auto-merge: feature merged into contentrain
|
|
115
|
+
--> update-ref advances baseBranch
|
|
116
|
+
--> selective sync .contentrain/ to developer's tree
|
|
117
|
+
--> review: feature pushed to remote, awaiting review
|
|
118
|
+
--> Worktree cleaned up
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The `contentrain` branch is permanent and protected. Feature branches are retained for `branchRetention` days (default: 30) for audit trail, then pruned.
|
|
122
|
+
|
|
123
|
+
### 3.6 Branch Health
|
|
124
|
+
|
|
125
|
+
MCP enforces branch health limits to prevent branch accumulation:
|
|
126
|
+
|
|
127
|
+
- **50+ active branches**: Warning. Operations continue but the user is alerted.
|
|
128
|
+
- **80+ active branches**: Blocked. No new write operations until branches are merged or deleted.
|
|
129
|
+
- `contentrain_status` reports branch health automatically (total, merged, unmerged counts).
|
|
130
|
+
- Merged branches are cleaned up lazily during status checks and submit operations.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 4. Conflict Resolution
|
|
135
|
+
|
|
136
|
+
### 4.1 How Conflicts Are Minimized
|
|
137
|
+
|
|
138
|
+
Contentrain's storage format is designed to minimize Git merge conflicts:
|
|
139
|
+
|
|
140
|
+
- **Object-map storage** for collections: each entry is a separate key-value pair. Two branches adding different entries rarely conflict.
|
|
141
|
+
- **Canonical serialization**: sorted keys and deterministic formatting prevent artificial diffs.
|
|
142
|
+
- **Review mode for normalize**: separate branches for extraction and reuse reduce concurrent editing conflicts.
|
|
143
|
+
|
|
144
|
+
### 4.2 When Conflicts Occur
|
|
145
|
+
|
|
146
|
+
| Scenario | Likelihood | Resolution |
|
|
147
|
+
|----------|-----------|------------|
|
|
148
|
+
| Different fields on the same entry | Low | Auto-merge succeeds (JSON field-level merge) |
|
|
149
|
+
| Same field changed by two branches | Medium | Studio conflict resolution UI |
|
|
150
|
+
| Two entries added with adjacent IDs | Very low | Auto-merge usually succeeds |
|
|
151
|
+
| Concurrent normalize operations | Avoided | Always review mode, sequential per model |
|
|
152
|
+
|
|
153
|
+
### 4.3 Conflict Resolution Rules
|
|
154
|
+
|
|
155
|
+
- Field-level merge for JSON: if the same entry has different fields changed in two branches, Git auto-merges.
|
|
156
|
+
- Same field changed by multiple branches creates a conflict requiring Studio resolution.
|
|
157
|
+
- When a conflict occurs, the agent should NOT attempt to resolve it. Inform the user and direct them to Studio.
|
|
158
|
+
- Collection object-map format with sorted keys means ~0.3% chance of conflict for two new entries in a 350-entry collection.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 5. Workflow States
|
|
163
|
+
|
|
164
|
+
Content entries move through these states:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
draft --> in_review --> published
|
|
168
|
+
|
|
|
169
|
+
v
|
|
170
|
+
rejected (with feedback)
|
|
171
|
+
|
|
172
|
+
published --> archived
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 5.1 State Definitions
|
|
176
|
+
|
|
177
|
+
| State | Git State | Trigger |
|
|
178
|
+
|-------|-----------|---------|
|
|
179
|
+
| `draft` | Branch exists, not yet reviewed/merged | Content created or updated |
|
|
180
|
+
| `in_review` | PR/branch open, labeled `contentrain-content` | `contentrain_submit` in review mode |
|
|
181
|
+
| `published` | Content is on `contentrain` branch and synced to baseBranch | Auto-merge, or PR merged after review |
|
|
182
|
+
| `rejected` | PR closed without merge | Studio reviewer rejects changes |
|
|
183
|
+
| `archived` | Metadata-only state | Manual action -- content hidden but retained |
|
|
184
|
+
|
|
185
|
+
### 5.2 State Rules
|
|
186
|
+
|
|
187
|
+
- **State is tracked in `.contentrain/meta/`, NOT in content files.** Agents never read or write state directly.
|
|
188
|
+
- **auto-merge mode:** `draft` --> `published` (no `in_review` step).
|
|
189
|
+
- **review mode:** `draft` --> `in_review` --> `published` or `rejected`.
|
|
190
|
+
- **Git is the source of truth.** States are derived from Git state (branch existence, merge status, PR status).
|
|
191
|
+
- Rejected content includes reviewer feedback. The agent can address feedback and resubmit.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 6. Validation Rules
|
|
196
|
+
|
|
197
|
+
### 6.1 When to Validate
|
|
198
|
+
|
|
199
|
+
- ALWAYS call `contentrain_validate` before `contentrain_submit`.
|
|
200
|
+
- `contentrain_submit` will fail if validation errors exist.
|
|
201
|
+
- Run validation after completing all changes in a batch, not after every individual save.
|
|
202
|
+
|
|
203
|
+
### 6.2 What Validation Checks
|
|
204
|
+
|
|
205
|
+
| Check | Severity | Description |
|
|
206
|
+
|-------|----------|-------------|
|
|
207
|
+
| Schema compliance | Error | Field values match their type definitions |
|
|
208
|
+
| Required fields | Error | All `required: true` fields have values |
|
|
209
|
+
| Unique constraints | Error | `unique: true` fields have no duplicates within the model |
|
|
210
|
+
| Locale completeness | Error/Warning | All supported locales have corresponding files and entries |
|
|
211
|
+
| Referential integrity | Error | Relation targets (IDs/slugs) exist in the target model |
|
|
212
|
+
| Canonical format | Warning | Files follow canonical serialization rules |
|
|
213
|
+
| Vocabulary usage | Warning | Content uses vocabulary terms when available |
|
|
214
|
+
|
|
215
|
+
### 6.3 Handling Validation Results
|
|
216
|
+
|
|
217
|
+
- **Errors:** MUST be fixed before submitting. Fix the content and re-validate.
|
|
218
|
+
- **Warnings:** Acceptable but should be addressed when possible. Acknowledge them before submitting.
|
|
219
|
+
- If validation returns zero errors, proceed with `contentrain_submit`.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 7. Submit Behavior
|
|
224
|
+
|
|
225
|
+
### 7.1 auto-merge Mode
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
contentrain_submit
|
|
229
|
+
--> Merge feature branch into contentrain
|
|
230
|
+
--> Advance baseBranch via update-ref (fast-forward)
|
|
231
|
+
--> Selectively sync .contentrain/ files to developer's working tree
|
|
232
|
+
--> Clean up worktree
|
|
233
|
+
--> Status: published
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 7.2 review Mode
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
contentrain_submit
|
|
240
|
+
--> Push feature branch to remote
|
|
241
|
+
--> Clean up worktree
|
|
242
|
+
--> Status: in_review
|
|
243
|
+
--> Studio notifies reviewers
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 7.3 Submit Rules
|
|
247
|
+
|
|
248
|
+
- Submit operates on the current pending branch. There must be pending changes.
|
|
249
|
+
- If no changes are pending, submit is a no-op.
|
|
250
|
+
- After submit, the agent can continue with other operations (new branch will be created for new changes).
|
|
251
|
+
- Normalize operations ALWAYS submit in review mode, regardless of project workflow setting.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 8. Metadata Structure
|
|
256
|
+
|
|
257
|
+
Metadata files track governance information. They are system-managed -- agents NEVER write to them.
|
|
258
|
+
|
|
259
|
+
### File Paths
|
|
260
|
+
|
|
261
|
+
| Kind | Path |
|
|
262
|
+
|------|------|
|
|
263
|
+
| Singleton / Dictionary | `.contentrain/meta/{modelId}/{locale}.json` |
|
|
264
|
+
| Collection | `.contentrain/meta/{modelId}/{locale}.json` (object-map: `{ entryId: meta }`) |
|
|
265
|
+
| Document | `.contentrain/meta/{modelId}/{slug}/{locale}.json` |
|
|
266
|
+
|
|
267
|
+
### Metadata Fields
|
|
268
|
+
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"status": "published",
|
|
272
|
+
"source": "agent",
|
|
273
|
+
"updated_by": "claude",
|
|
274
|
+
"approved_by": "ahmet@contentrain.io",
|
|
275
|
+
"version": "1"
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
| Field | Values | Description |
|
|
280
|
+
|-------|--------|-------------|
|
|
281
|
+
| `status` | `draft`, `in_review`, `published`, `rejected`, `archived` | Current workflow state |
|
|
282
|
+
| `source` | `agent`, `human`, `import` | How the content was created |
|
|
283
|
+
| `updated_by` | string | Agent name or user email |
|
|
284
|
+
| `approved_by` | string or null | Who approved (review mode only) |
|
|
285
|
+
| `version` | string | Content version identifier |
|