@bendyline/gilde 0.1.21 → 0.1.22
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/authoring/chat-models/nemotron3.5-lightning-30b-q4.json +3 -2
- package/authoring/chat-models/nemotron3.5-lightning-30b-q8.json +3 -2
- package/authoring/page-demo-stub.js +85 -0
- package/data/chat-models/index.json +1 -1
- package/data/chat-models/ne/nemotron3.5-lightning-30b-q4/manifest.json +3 -2
- package/data/chat-models/ne/nemotron3.5-lightning-30b-q8/manifest.json +3 -2
- package/data/connector-types/bl/bluesky-posts/manifest.json +16 -0
- package/data/connector-types/bl/bluesky-posts/versions/1.0.0/manifest.json +70 -0
- package/data/connector-types/gi/github-issues/versions/1.0.1/manifest.json +67 -0
- package/data/connector-types/in/instagram-media/manifest.json +17 -0
- package/data/connector-types/in/instagram-media/versions/1.0.0/manifest.json +70 -0
- package/data/connector-types/index.json +1 -1
- package/data/connector-types/x-/x-posts/manifest.json +17 -0
- package/data/connector-types/x-/x-posts/versions/1.0.0/manifest.json +66 -0
- package/data/craftbook-templates/dr/draft-social-post/manifest.json +23 -0
- package/data/craftbook-templates/dr/draft-social-post/versions/1.0.0/craftbook.json +176 -0
- package/data/craftbook-templates/dr/draft-social-post/versions/1.0.0/test.json +161 -0
- package/data/craftbook-templates/index.json +1 -1
- package/data/craftbook-templates/re/reception-report/manifest.json +21 -0
- package/data/craftbook-templates/re/reception-report/versions/1.0.0/craftbook.json +170 -0
- package/data/craftbook-templates/re/reception-report/versions/1.0.0/test.json +168 -0
- package/data/craftbook-templates/so/social-digest/manifest.json +21 -0
- package/data/craftbook-templates/so/social-digest/versions/1.0.0/craftbook.json +133 -0
- package/data/craftbook-templates/so/social-digest/versions/1.0.0/test.json +168 -0
- package/data/gezel-templates/index.json +1 -1
- package/data/gezel-templates/om/omroeper/manifest.json +20 -0
- package/data/gezel-templates/om/omroeper/versions/1.0.0/about.md +33 -0
- package/data/gezel-templates/om/omroeper/versions/1.0.0/manifest.json +23 -0
- package/data/project-types/im/image-feed/manifest.json +20 -0
- package/data/project-types/im/image-feed/versions/1.0.0/about.md +21 -0
- package/data/project-types/im/image-feed/versions/1.0.0/manifest.json +330 -0
- package/data/project-types/im/image-feed/versions/1.0.0/mission.md +10 -0
- package/data/project-types/im/image-feed/versions/1.0.0/pages/gallery/index.html +831 -0
- package/data/project-types/im/image-feed/versions/1.0.0/posts/index.json +4 -0
- package/data/project-types/im/image-feed/versions/1.0.0/style-guide.md +24 -0
- package/data/project-types/index.json +1 -1
- package/data/project-types/so/social-feed/manifest.json +20 -0
- package/data/project-types/so/social-feed/versions/1.0.0/about.md +19 -0
- package/data/project-types/so/social-feed/versions/1.0.0/manifest.json +330 -0
- package/data/project-types/so/social-feed/versions/1.0.0/mission.md +10 -0
- package/data/project-types/so/social-feed/versions/1.0.0/pages/dashboard/index.html +754 -0
- package/data/project-types/so/social-feed/versions/1.0.0/posts/index.json +4 -0
- package/data/project-types/so/social-feed/versions/1.0.0/voice-guide.md +22 -0
- package/package.json +1 -1
- package/schemas/connector-type-version.schema.json +6 -0
- package/schemas/craftbook-test.schema.json +1 -0
- package/schemas/project-type-version.schema.json +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "craftbook-template",
|
|
4
|
+
"id": "reception-report",
|
|
5
|
+
"role": "maintenance-review",
|
|
6
|
+
"name": "Reception Report",
|
|
7
|
+
"description": "The weekly reception review for your social posting. Reconciles published posts with their live feed records (stamping the corpus reference onto any post that lost track of its live copy), builds a per-post engagement ledger of likes, reposts, and replies, compares each post against your account's recent baseline, and reports what landed, what did not, and what to post next — closing with a single one-click action that seeds the next draft from the strongest theme. Covers social media analytics, post performance review, engagement reporting, cadence tracking, Bluesky and X and Instagram metrics, and honest small-sample reading of your feeds. No connected feed? It still writes the ledger from what the workspace knows and names the gap.",
|
|
8
|
+
"tags": [
|
|
9
|
+
"social",
|
|
10
|
+
"analytics",
|
|
11
|
+
"engagement",
|
|
12
|
+
"report",
|
|
13
|
+
"weekly"
|
|
14
|
+
],
|
|
15
|
+
"maintainer": {
|
|
16
|
+
"name": "Gezel"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"yankedVersions": [],
|
|
20
|
+
"workflow": "build-loop"
|
|
21
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "reception-report",
|
|
3
|
+
"name": "Reception Report",
|
|
4
|
+
"description": "Weekly review of how the posting landed: reconcile published posts with their live synced records (stamp corpusRef where it is missing), build a per-post engagement ledger, analyze each post against the account's recent baseline, then write the reception report — scoreboard, three findings, three recommendations, and exactly one one-click action seeding the next draft-social-post run with the strongest theme. Every metric must trace to the ledger, and the ledger to a synced record. Runs gracefully with no connector bound: the ledger then carries what the workspace knows, with metric cells marked as unknown.",
|
|
5
|
+
"entryStepId": "reconcile",
|
|
6
|
+
"triggers": [
|
|
7
|
+
"reception report",
|
|
8
|
+
"how did our posts do",
|
|
9
|
+
"social performance review",
|
|
10
|
+
"weekly social report"
|
|
11
|
+
],
|
|
12
|
+
"command": "reception-report",
|
|
13
|
+
"paramSchema": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"lookbackDays": {
|
|
17
|
+
"type": "integer",
|
|
18
|
+
"default": 7,
|
|
19
|
+
"description": "How many days of posting the review covers. Default 7."
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"runModes": {
|
|
24
|
+
"scheduled": "recommended",
|
|
25
|
+
"nightShift": "supported"
|
|
26
|
+
},
|
|
27
|
+
"connectors": [
|
|
28
|
+
{
|
|
29
|
+
"typeId": "bluesky-posts",
|
|
30
|
+
"optional": true,
|
|
31
|
+
"reason": "Match published posts to their live Bluesky records and read the engagement counts the ledger reports."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"typeId": "x-posts",
|
|
35
|
+
"optional": true,
|
|
36
|
+
"reason": "Match published posts to their live X records and read the engagement counts the ledger reports."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"typeId": "instagram-media",
|
|
40
|
+
"optional": true,
|
|
41
|
+
"reason": "Read the synced Instagram records so caption performance can join the ledger."
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"steps": [
|
|
45
|
+
{
|
|
46
|
+
"id": "reconcile",
|
|
47
|
+
"name": "Reconcile posts with live records",
|
|
48
|
+
"description": "Stamp missing corpusRef links and build the engagement ledger from the synced records.",
|
|
49
|
+
"suggestedRole": "omroeper",
|
|
50
|
+
"prompt": "You are reconciling our published posts with their live feed records, looking back {{lookbackDays}} days (if that value reads as a raw {{...}} placeholder, use the default: 7).\n\n1. Read `posts/index.json` with `read_file`, then each listed post folder's `post.md` and every file under its `variants/`. Collect every variant whose `status` is `published` or `scheduled`.\n2. For each collected variant MISSING a `corpusRef` in its frontmatter: find its live record in the project artifacts under `data/` (`list_artifacts`, then `read_artifact`) by matching the variant's `postId` or `permalink` against the record frontmatter — fall back to matching the opening text. When found, stamp it: call `record_publication` if it is in your function schema; otherwise edit the variant file with `write_file`, adding `corpusRef: <artifacts-relative record path>` (and `publishedAt` from the record when the variant lacks it). Leave variants that already carry `corpusRef` untouched.\n3. If there is no `data/` corpus at all, note \"no synced corpus — reconciliation skipped\" and still write the ledger from what the workspace knows, with unknown metric cells marked `-`.\n4. Write the artifact `reports/reception-ledger.md` with `write_artifact`: a markdown table, one row per published or scheduled variant, columns `Post` (slug), `Platform`, `Live record` (the corpusRef path or `-`), `Likes`, `Reposts`, `Replies`, `Metrics as of` (the record's `metrics_updated_at`). Copy every count verbatim from its record; use `-` where unknown. Below the table, add one line per reconciliation you performed this run.\n\nWhen the ledger artifact is written, call `advance_task_step` to move to analyze.",
|
|
51
|
+
"advanceWhen": {
|
|
52
|
+
"file": "reports/reception-ledger.md",
|
|
53
|
+
"artifact": true,
|
|
54
|
+
"minBytes": 150
|
|
55
|
+
},
|
|
56
|
+
"gate": {
|
|
57
|
+
"at": "completion",
|
|
58
|
+
"checks": [
|
|
59
|
+
{
|
|
60
|
+
"kind": "minBytes",
|
|
61
|
+
"file": "reports/reception-ledger.md",
|
|
62
|
+
"bytes": 150,
|
|
63
|
+
"artifact": true
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"maxAttempts": 3
|
|
67
|
+
},
|
|
68
|
+
"next": "analyze"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "analyze",
|
|
72
|
+
"name": "Analyze reception",
|
|
73
|
+
"description": "Read the ledger and work out what over- and under-performed against the account's own baseline.",
|
|
74
|
+
"suggestedRole": "omroeper",
|
|
75
|
+
"prompt": "Read `reports/reception-ledger.md` with `read_artifact`. Every number in your analysis must come from that ledger — cite the post slug next to each figure; no other source of numbers is allowed.\n\nWrite the artifact `reports/reception-analysis.md` with `write_artifact`:\n\n- `## Per-post` — each post against the account's recent baseline. The median of the ledger's like/repost/reply counts is a fine baseline; name the baseline you used.\n- `## What worked, what did not` — formats, themes, or posting times that over- or under-performed, each claim tied to specific ledger rows.\n- `## Cadence` — posts shipped in the window versus the project's cadence target when the mission or voice guide states one; when none is stated, say so and suggest a target.\n\nBe honest about small samples: with only a handful of posts, call patterns \"early signals\", never conclusions. When the analysis artifact is written, call `advance_task_step` to move to report.",
|
|
76
|
+
"advanceWhen": {
|
|
77
|
+
"file": "reports/reception-analysis.md",
|
|
78
|
+
"artifact": true,
|
|
79
|
+
"minBytes": 300
|
|
80
|
+
},
|
|
81
|
+
"gate": {
|
|
82
|
+
"at": "completion",
|
|
83
|
+
"checks": [
|
|
84
|
+
{
|
|
85
|
+
"kind": "minBytes",
|
|
86
|
+
"file": "reports/reception-analysis.md",
|
|
87
|
+
"bytes": 300,
|
|
88
|
+
"artifact": true
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"kind": "judge",
|
|
92
|
+
"file": "reports/reception-analysis.md",
|
|
93
|
+
"rubric": "Every metric named in the analysis appears in the ledger; baselines are computed from ledger rows, not invented; claims cite post slugs.",
|
|
94
|
+
"sourceFiles": [
|
|
95
|
+
"reports/reception-ledger.md"
|
|
96
|
+
],
|
|
97
|
+
"label": "no metric absent from the ledger",
|
|
98
|
+
"artifact": true
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"onReject": "analyze",
|
|
102
|
+
"maxAttempts": 3
|
|
103
|
+
},
|
|
104
|
+
"next": "report"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "report",
|
|
108
|
+
"name": "Write the reception report",
|
|
109
|
+
"description": "Scoreboard, three findings, three recommendations, and exactly one one-click next-post action.",
|
|
110
|
+
"suggestedRole": "omroeper",
|
|
111
|
+
"prompt": "Assemble the reception report from `reports/reception-ledger.md` and `reports/reception-analysis.md` (read both with `read_artifact`). Cite no number that is not in the ledger.\n\nWrite the report to the artifact `reports/reception-report-<today YYYY-MM-DD>.md` with `write_artifact` (use today's real date in the filename), then write the exact same content to `reports/reception-report-latest.md` — the stable path the completion gate reads. Write the latest file last. Structure:\n\n- `## Scoreboard` — the ledger table trimmed to `Post`, `Platform`, `Likes`, `Reposts`, `Replies`.\n- `## Findings` — exactly 3, each one sentence plus one line of supporting evidence from the ledger or analysis.\n- `## Recommendations` — exactly 3, each concrete enough to act on this week.\n- `## Next post` — one paragraph naming the strongest theme to post about next, followed by EXACTLY ONE ```gezel-action fenced YAML block. Rules for the block: flat YAML keys (only `params` nests one level); a unique `id` slug that stays stable across weekly runs unless the theme changes; short `title`; one-sentence `reason`; never invent craftbook ids — reference only `draft-social-post`, which ships alongside this workflow:\n\n```gezel-action\nkind: fire-craftbook\nid: next-post-<theme-slug>\ntitle: Draft the next post\nreason: <the strongest theme, grounded in the findings>\ncraftbookId: draft-social-post\nparams:\n topic: <the suggested topic>\n```\n\nWhen both report files are written, call `advance_task_step` to move to evaluate.",
|
|
112
|
+
"advanceWhen": {
|
|
113
|
+
"file": "reports/reception-report-latest.md",
|
|
114
|
+
"artifact": true,
|
|
115
|
+
"minBytes": 400,
|
|
116
|
+
"requireChange": true
|
|
117
|
+
},
|
|
118
|
+
"gate": {
|
|
119
|
+
"at": "completion",
|
|
120
|
+
"checks": [
|
|
121
|
+
{
|
|
122
|
+
"kind": "minBytes",
|
|
123
|
+
"file": "reports/reception-report-latest.md",
|
|
124
|
+
"bytes": 400,
|
|
125
|
+
"artifact": true
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"kind": "contains",
|
|
129
|
+
"file": "reports/reception-report-latest.md",
|
|
130
|
+
"pattern": "```gezel-action",
|
|
131
|
+
"label": "report carries the one-click next-post action",
|
|
132
|
+
"artifact": true
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"kind": "judge",
|
|
136
|
+
"file": "reports/reception-report-latest.md",
|
|
137
|
+
"rubric": "Every metric in the report appears in the ledger; the findings follow from the analysis; there is exactly one gezel-action block and it references craftbookId draft-social-post with a topic grounded in the findings.",
|
|
138
|
+
"sourceFiles": [
|
|
139
|
+
"reports/reception-ledger.md",
|
|
140
|
+
"reports/reception-analysis.md"
|
|
141
|
+
],
|
|
142
|
+
"label": "grounded scoreboard and single action",
|
|
143
|
+
"artifact": true
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"onReject": "report",
|
|
147
|
+
"maxAttempts": 4
|
|
148
|
+
},
|
|
149
|
+
"next": "evaluate"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "evaluate",
|
|
153
|
+
"name": "Evaluate",
|
|
154
|
+
"description": "Grade the report against every criterion. All pass — finish; any fail — loop back to report with the named gaps.",
|
|
155
|
+
"suggestedRole": "reviewer",
|
|
156
|
+
"prompt": "Open `reports/reception-report-latest.md`, `reports/reception-analysis.md`, and `reports/reception-ledger.md` with `read_artifact` and grade the report. Check EACH criterion and write PASS/FAIL with a one-line reason:\n\n1. The scoreboard covers every ledger row (no post silently dropped).\n2. Every number in the report traces to the ledger (spot-check at least three).\n3. There are exactly 3 findings, each with evidence.\n4. There are exactly 3 recommendations, each actionable this week.\n5. There is exactly one ```gezel-action block, it is valid flat YAML, and its `craftbookId` is `draft-social-post` with a `params.topic` seeded from the strongest theme.\n6. Small samples are read honestly — early signals, not sweeping conclusions.\n\nThen route — this is the whole point of the loop:\n\n- **Every criterion PASSES →** call `advance_task_step({ ref, stepId: \"evaluate\", next: \"finish\" })`.\n- **Any criterion FAILS →** write the specific gaps to task notes with `write_task_note`, then call `advance_task_step({ ref, stepId: \"evaluate\", next: \"report\" })` to loop back. The report step fixes exactly those gaps.\n\nNever route to finish while a criterion is unmet. After ~3 unproductive loops, stop and report DONE_WITH_CONCERNS so the user can step in.",
|
|
157
|
+
"next": "report"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": "finish",
|
|
161
|
+
"name": "Finish",
|
|
162
|
+
"description": "Stamp the summary note and stop.",
|
|
163
|
+
"suggestedRole": "omroeper",
|
|
164
|
+
"prompt": "Write a one-paragraph DONE summary with `write_task_note`: the report artifact paths (the dated file and `reports/reception-report-latest.md`), the top finding in one line, how many posts were reconciled this run, and the recommended next topic. Report DONE.",
|
|
165
|
+
"terminal": true
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"version": "1.0.0",
|
|
169
|
+
"releasedAt": "2026-08-12T00:00:00Z"
|
|
170
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"title": "Weekly reception review with a reconciliation gap",
|
|
4
|
+
"objective": "Prove that reception-report stamps the missing corpusRef onto a published post, builds a ledger covering every published post, and writes a grounded report with exactly one next-post action.",
|
|
5
|
+
"tags": [
|
|
6
|
+
"corpus"
|
|
7
|
+
],
|
|
8
|
+
"prompt": "Run the Reception Report craftbook for this project. Treat every synced record in the artifacts drawer as inside the review window (the lookback parameter is set very wide for this eval). One published post — returns-faq — is missing its corpusRef; reconcile it against the synced records, build the reception ledger covering all three published posts, and write the weekly reception report with its single one-click next-post action.",
|
|
9
|
+
"setup": {
|
|
10
|
+
"projectName": "Reception Report Eval",
|
|
11
|
+
"about": "Weekly reception review eval for a small furniture workshop's Bluesky presence. Three published posts sit in the workspace posts tree — one missing its corpusRef — and their live records with engagement counts sit in the artifacts drawer.",
|
|
12
|
+
"missionObjectives": "Reconcile posts/2026-08-08-returns-faq/variants/bluesky.md with its live record (stamp corpusRef), build reports/reception-ledger.md covering summer-sale, behind-the-scenes, and returns-faq with verbatim counts, analyze against the account baseline, and write the reception report with exactly one gezel-action block firing draft-social-post.",
|
|
13
|
+
"files": [
|
|
14
|
+
{
|
|
15
|
+
"path": "posts/index.json",
|
|
16
|
+
"content": "{\n \"entries\": [\n {\n \"slug\": \"summer-sale\",\n \"folder\": \"posts/2026-07-28-summer-sale\",\n \"title\": \"Summer sale on seconds\",\n \"status\": \"published\",\n \"platforms\": [\"bluesky\"],\n \"created\": \"2026-07-28\"\n },\n {\n \"slug\": \"behind-the-scenes\",\n \"folder\": \"posts/2026-08-03-behind-the-scenes\",\n \"title\": \"Behind the scenes at the bench\",\n \"status\": \"published\",\n \"platforms\": [\"bluesky\"],\n \"created\": \"2026-08-03\"\n },\n {\n \"slug\": \"returns-faq\",\n \"folder\": \"posts/2026-08-08-returns-faq\",\n \"title\": \"Your returns questions, answered\",\n \"status\": \"published\",\n \"platforms\": [\"bluesky\"],\n \"created\": \"2026-08-08\"\n }\n ]\n}\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "posts/2026-07-28-summer-sale/post.md",
|
|
20
|
+
"content": "---\nstatus: published\ntitle: Summer sale on seconds\nslug: summer-sale\nplatforms: [bluesky]\ncreated: 2026-07-28\ntags: [sale]\n---\nSeconds shelf is full: benches and boxes with honest scars, priced to leave. Ends Sunday.\n"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"path": "posts/2026-07-28-summer-sale/variants/bluesky.md",
|
|
24
|
+
"content": "---\nplatform: bluesky\ncharCount: 96\nstatus: published\npublishedAt: 2026-07-28T09:00:00Z\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3ksale001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3ksale001\ncorpusRef: data/bluesky/posts/2026-07/2026-07-28-summer-sale.md\n---\nSeconds shelf is full: benches and boxes with honest scars, priced to leave. Ends Sunday.\n"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": "posts/2026-08-03-behind-the-scenes/post.md",
|
|
28
|
+
"content": "---\nstatus: published\ntitle: Behind the scenes at the bench\nslug: behind-the-scenes\nplatforms: [bluesky]\ncreated: 2026-08-03\ntags: [workshop]\n---\nWhat a Tuesday actually looks like here: glue squeeze-out, sharpening breaks, and the radio arguing with itself.\n"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "posts/2026-08-03-behind-the-scenes/variants/bluesky.md",
|
|
32
|
+
"content": "---\nplatform: bluesky\ncharCount: 113\nstatus: published\npublishedAt: 2026-08-03T10:30:00Z\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3kbts0001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3kbts0001\ncorpusRef: data/bluesky/posts/2026-08/2026-08-03-behind-the-scenes.md\n---\nWhat a Tuesday actually looks like here: glue squeeze-out, sharpening breaks, and the radio arguing with itself.\n"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"path": "posts/2026-08-08-returns-faq/post.md",
|
|
36
|
+
"content": "---\nstatus: published\ntitle: Your returns questions, answered\nslug: returns-faq\nplatforms: [bluesky]\ncreated: 2026-08-08\ntags: [returns]\n---\nYour returns questions, answered: what the new desk takes back, how long it takes, and what needs no receipt.\n"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"path": "posts/2026-08-08-returns-faq/variants/bluesky.md",
|
|
40
|
+
"content": "---\nplatform: bluesky\ncharCount: 109\nstatus: published\npublishedAt: 2026-08-08T09:15:00Z\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3kfaq0001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3kfaq0001\n---\nYour returns questions, answered: what the new desk takes back, how long it takes, and what needs no receipt.\n"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"path": "data/bluesky/_meta.json",
|
|
44
|
+
"surface": "artifact",
|
|
45
|
+
"content": "{\n \"bindingId\": \"bluesky-workshop\",\n \"typeId\": \"bluesky-posts\",\n \"displayName\": \"Bluesky — @bankenbeitel.bsky.social\",\n \"completeness\": \"window\",\n \"lastSyncedAt\": \"2026-08-12T05:00:00Z\"\n}\n"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"path": "data/bluesky/posts/2026-07/2026-07-28-summer-sale.md",
|
|
49
|
+
"surface": "artifact",
|
|
50
|
+
"content": "---\nrecord: post\nplatform: bluesky\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3ksale001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3ksale001\nauthor: bankenbeitel.bsky.social\ncreatedAt: 2026-07-28T09:00:00Z\nlikes: 18\nreposts: 3\nreplies: 2\nmetrics_updated_at: 2026-08-12T05:00:00Z\n---\nSeconds shelf is full: benches and boxes with honest scars, priced to leave. Ends Sunday.\n"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"path": "data/bluesky/posts/2026-08/2026-08-03-behind-the-scenes.md",
|
|
54
|
+
"surface": "artifact",
|
|
55
|
+
"content": "---\nrecord: post\nplatform: bluesky\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3kbts0001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3kbts0001\nauthor: bankenbeitel.bsky.social\ncreatedAt: 2026-08-03T10:30:00Z\nlikes: 57\nreposts: 21\nreplies: 9\nmetrics_updated_at: 2026-08-12T05:00:00Z\n---\nWhat a Tuesday actually looks like here: glue squeeze-out, sharpening breaks, and the radio arguing with itself.\n"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"path": "data/bluesky/posts/2026-08/2026-08-08-returns-faq.md",
|
|
59
|
+
"surface": "artifact",
|
|
60
|
+
"content": "---\nrecord: post\nplatform: bluesky\npostId: at://did:plc:bankenbeitel/app.bsky.feed.post/3kfaq0001\npermalink: https://bsky.app/profile/bankenbeitel.bsky.social/post/3kfaq0001\nauthor: bankenbeitel.bsky.social\ncreatedAt: 2026-08-08T09:15:00Z\nlikes: 24\nreposts: 6\nreplies: 11\nmetrics_updated_at: 2026-08-12T05:00:00Z\n---\nYour returns questions, answered: what the new desk takes back, how long it takes, and what needs no receipt.\n"
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"craftbookParams": {
|
|
64
|
+
"lookbackDays": "3650"
|
|
65
|
+
},
|
|
66
|
+
"worker": {
|
|
67
|
+
"name": "Sten",
|
|
68
|
+
"role": "Omroeper"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"mocks": [],
|
|
72
|
+
"success": {
|
|
73
|
+
"summary": "The returns-faq variant gained its corpusRef, the ledger covers all three published posts with verbatim counts, and the reception report at its stable path carries exactly one gezel-action block firing draft-social-post.",
|
|
74
|
+
"checks": [
|
|
75
|
+
{
|
|
76
|
+
"kind": "contains",
|
|
77
|
+
"file": "posts/2026-08-08-returns-faq/variants/bluesky.md",
|
|
78
|
+
"pattern": "corpusRef:",
|
|
79
|
+
"label": "the missing corpusRef was reconciled"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"kind": "minBytes",
|
|
83
|
+
"file": "reports/reception-ledger.md",
|
|
84
|
+
"bytes": 150,
|
|
85
|
+
"artifact": true
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"kind": "contains",
|
|
89
|
+
"file": "reports/reception-ledger.md",
|
|
90
|
+
"pattern": "summer-sale",
|
|
91
|
+
"label": "ledger covers summer-sale",
|
|
92
|
+
"artifact": true
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"kind": "contains",
|
|
96
|
+
"file": "reports/reception-ledger.md",
|
|
97
|
+
"pattern": "behind-the-scenes",
|
|
98
|
+
"label": "ledger covers behind-the-scenes",
|
|
99
|
+
"artifact": true
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"kind": "contains",
|
|
103
|
+
"file": "reports/reception-ledger.md",
|
|
104
|
+
"pattern": "returns-faq",
|
|
105
|
+
"label": "ledger covers returns-faq",
|
|
106
|
+
"artifact": true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"kind": "minBytes",
|
|
110
|
+
"file": "reports/reception-report-latest.md",
|
|
111
|
+
"bytes": 400,
|
|
112
|
+
"artifact": true
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"kind": "contains",
|
|
116
|
+
"file": "reports/reception-report-latest.md",
|
|
117
|
+
"pattern": "```gezel-action",
|
|
118
|
+
"label": "report carries the next-post action",
|
|
119
|
+
"artifact": true
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"kind": "contains",
|
|
123
|
+
"file": "reports/reception-report-latest.md",
|
|
124
|
+
"pattern": "craftbookId: draft-social-post",
|
|
125
|
+
"label": "the action fires draft-social-post",
|
|
126
|
+
"artifact": true
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"taskGraph": {
|
|
130
|
+
"requireCraftbookTask": true,
|
|
131
|
+
"requireTerminalStep": true
|
|
132
|
+
},
|
|
133
|
+
"unchangedFixtures": [
|
|
134
|
+
"posts/index.json",
|
|
135
|
+
"posts/2026-07-28-summer-sale/post.md",
|
|
136
|
+
"posts/2026-07-28-summer-sale/variants/bluesky.md",
|
|
137
|
+
"posts/2026-08-03-behind-the-scenes/post.md",
|
|
138
|
+
"posts/2026-08-03-behind-the-scenes/variants/bluesky.md",
|
|
139
|
+
"posts/2026-08-08-returns-faq/post.md"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"rubric": {
|
|
143
|
+
"artifact": {
|
|
144
|
+
"path": "reports/reception-report-latest.md",
|
|
145
|
+
"kind": "markdown"
|
|
146
|
+
},
|
|
147
|
+
"axes": [
|
|
148
|
+
{
|
|
149
|
+
"name": "grounding",
|
|
150
|
+
"description": "No metric appears in the report that is absent from the ledger; the ledger counts match the seeded records verbatim (18/3/2, 57/21/9, 24/6/11)."
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"name": "insight",
|
|
154
|
+
"description": "The findings read the data honestly: behind-the-scenes over-performed, returns-faq drew reply-heavy engagement, and small-sample caveats are stated."
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "actionability",
|
|
158
|
+
"description": "Exactly one gezel-action block, valid flat YAML with a stable id, firing draft-social-post with a topic seeded from the strongest theme."
|
|
159
|
+
}
|
|
160
|
+
],
|
|
161
|
+
"contextNote": "Only the returns-faq bluesky variant should gain frontmatter this run; the other seeded post files stay byte-identical. The report is an artifacts-drawer file."
|
|
162
|
+
},
|
|
163
|
+
"qualityFocus": [
|
|
164
|
+
"reconciliation touches only the variant missing corpusRef",
|
|
165
|
+
"ledger counts copied verbatim from records",
|
|
166
|
+
"exactly one next-post action, never inventing craftbook ids"
|
|
167
|
+
]
|
|
168
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"kind": "craftbook-template",
|
|
4
|
+
"id": "social-digest",
|
|
5
|
+
"role": "maintenance-review",
|
|
6
|
+
"name": "Social Digest",
|
|
7
|
+
"description": "The overnight read of your social feeds. Scans the synced Bluesky, X, and Instagram corpus records since the last look plus your own post index, then leaves a morning digest: what happened on each feed, notable mentions and replies with the people who wrote them, which posts moved on likes, reposts, and replies, and suggested next moves — each carrying a one-click action to draft the follow-up post or a reply task. Covers social media monitoring, engagement tracking, mention triage, and a nightly social summary grounded entirely in real synced records. No connected feed? It still runs, reports what the workspace knows, and names the gap instead of inventing activity.",
|
|
8
|
+
"tags": [
|
|
9
|
+
"social",
|
|
10
|
+
"digest",
|
|
11
|
+
"engagement",
|
|
12
|
+
"night-shift",
|
|
13
|
+
"analytics"
|
|
14
|
+
],
|
|
15
|
+
"maintainer": {
|
|
16
|
+
"name": "Gezel"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"yankedVersions": [],
|
|
20
|
+
"workflow": "build-loop"
|
|
21
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "social-digest",
|
|
3
|
+
"name": "Social Digest",
|
|
4
|
+
"description": "Read everything new on the synced social feeds since the lookback window opened, then compose a grounded morning digest: per-feed activity, notable mentions and replies, engagement movers, and 1-3 suggested next moves carrying one-click gezel-action blocks (fire the draft-social-post craftbook with a prefilled topic, or a reply task for a mention worth answering). Every number in the digest must trace to a synced record via the scan notes — no invented metrics. Runs happily with no connector bound: it reports from posts/index.json alone and names the missing corpus as a gap.",
|
|
5
|
+
"entryStepId": "scan",
|
|
6
|
+
"triggers": [
|
|
7
|
+
"social digest",
|
|
8
|
+
"what happened on our feeds",
|
|
9
|
+
"engagement digest",
|
|
10
|
+
"overnight social summary"
|
|
11
|
+
],
|
|
12
|
+
"command": "social-digest",
|
|
13
|
+
"paramSchema": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"lookbackHours": {
|
|
17
|
+
"type": "integer",
|
|
18
|
+
"default": 24,
|
|
19
|
+
"description": "How many hours back to scan for new records. Default 24."
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"runModes": {
|
|
24
|
+
"nightShift": "recommended",
|
|
25
|
+
"scheduled": "supported"
|
|
26
|
+
},
|
|
27
|
+
"connectors": [
|
|
28
|
+
{
|
|
29
|
+
"typeId": "bluesky-posts",
|
|
30
|
+
"optional": true,
|
|
31
|
+
"reason": "Read the synced Bluesky posts, mentions, and engagement counts the digest reports on."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"typeId": "x-posts",
|
|
35
|
+
"optional": true,
|
|
36
|
+
"reason": "Read the synced X posts, mentions, and engagement counts the digest reports on."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"typeId": "instagram-media",
|
|
40
|
+
"optional": true,
|
|
41
|
+
"reason": "Read the synced Instagram records so captions and comments show up in the digest."
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"steps": [
|
|
45
|
+
{
|
|
46
|
+
"id": "scan",
|
|
47
|
+
"name": "Scan the feeds",
|
|
48
|
+
"description": "Collect raw notes from every synced corpus record in the window — the only numbers the digest may cite.",
|
|
49
|
+
"suggestedRole": "omroeper",
|
|
50
|
+
"prompt": "You are collecting the raw material for the overnight social digest. Look back {{lookbackHours}} hours (if that value reads as a raw {{...}} placeholder, use the default: 24).\n\n1. Call `list_artifacts` on `data/` in the project artifacts. Each subfolder there is one synced feed corpus (Bluesky, X, Instagram, ...); its `_meta.json` names the feed and the last sync. If there is no `data/` folder, nothing is synced: still write the notes artifact, open it with the line \"no synced corpus this run\", record what `posts/index.json` says we published lately, and never invent feed activity.\n2. Read every record inside the window with `read_artifact` — records are markdown files with frontmatter under each corpus, organized newest-first by folder. Collect per feed: posts by us, mentions and replies (author handle plus a short verbatim quote), and each record's engagement counts (`likes`, `reposts`, `replies`) together with its `metrics_updated_at` stamp.\n3. Read `posts/index.json` in the workspace with `read_file` so the digest can match feed records to the posts we shipped.\n4. Write your raw notes to the artifact `reports/social-digest-notes.md` with `write_artifact`: one section per feed, and every count copied verbatim next to the record path it came from (for example `data/bluesky/posts/2026-08/2026-08-02-meet-the-makers.md`). These notes are the only source of numbers the digest step may cite.\n\nWhen the notes artifact is written, call `advance_task_step` to move to compose.",
|
|
51
|
+
"advanceWhen": {
|
|
52
|
+
"file": "reports/social-digest-notes.md",
|
|
53
|
+
"artifact": true,
|
|
54
|
+
"minBytes": 150
|
|
55
|
+
},
|
|
56
|
+
"gate": {
|
|
57
|
+
"at": "completion",
|
|
58
|
+
"checks": [
|
|
59
|
+
{
|
|
60
|
+
"kind": "minBytes",
|
|
61
|
+
"file": "reports/social-digest-notes.md",
|
|
62
|
+
"bytes": 150,
|
|
63
|
+
"artifact": true
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"maxAttempts": 3
|
|
67
|
+
},
|
|
68
|
+
"next": "compose"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "compose",
|
|
72
|
+
"name": "Compose the digest",
|
|
73
|
+
"description": "Write the morning digest from the scan notes, ending with 1-3 one-click actions.",
|
|
74
|
+
"suggestedRole": "omroeper",
|
|
75
|
+
"prompt": "Write the digest from `reports/social-digest-notes.md` (read it with `read_artifact` first). Every number you cite must appear in those notes — no other source of numbers is allowed.\n\nWrite the digest to the artifact `reports/social-digest-<today YYYY-MM-DD>.md` with `write_artifact` (use today's real date in the filename), then write the exact same content to `reports/social-digest-latest.md` — the stable path the completion gate reads. Write the latest file last. Sections, in order:\n\n- `## What happened on each feed` — one short block per synced feed; when a feed has no corpus or no new activity, say so in one line.\n- `## Notable mentions & replies` — who said what: name each author, quote them briefly, and say whether the mention deserves a reply.\n- `## Engagement movers` — which of our posts moved: compare the engagement counts and their `metrics_updated_at` stamps from the notes, and call out over- and under-performers. Never invent a number.\n- `## Suggested next moves` — 1-3 concrete suggestions. When a suggestion is directly actionable, follow it with a ```gezel-action fenced YAML block so the user can fire it with one click in the morning. Rules for these blocks: one block per action; flat YAML keys (only `params` nests one level); each block carries a unique, stable `id` slug that stays the same when tomorrow's digest repeats the suggestion; keep `title` short and `reason` to a sentence or two; never invent craftbook ids — the only craftbook you may reference is `draft-social-post`, which ships alongside this workflow. Two kinds:\n\nTo suggest drafting a post (prefill the topic):\n```gezel-action\nkind: fire-craftbook\nid: draft-<short-topic-slug>\ntitle: Draft a post about <thing>\nreason: <why, one sentence grounded in the notes>\ncraftbookId: draft-social-post\nparams:\n topic: <the suggested topic>\n```\n\nTo suggest replying to a mention (a bespoke task with a concrete prompt):\n```gezel-action\nkind: create-task\nid: reply-<author-slug>\ntitle: Reply to <author>\nreason: <why this mention matters, one sentence>\nprompt: Read <record path> in the project artifacts and draft a reply in our voice for the user to approve. Do not post it.\nrole: omroeper\n```\n\nWhen both digest files are written, call `advance_task_step` to move to evaluate.",
|
|
76
|
+
"advanceWhen": {
|
|
77
|
+
"file": "reports/social-digest-latest.md",
|
|
78
|
+
"artifact": true,
|
|
79
|
+
"minBytes": 400,
|
|
80
|
+
"requireChange": true
|
|
81
|
+
},
|
|
82
|
+
"gate": {
|
|
83
|
+
"at": "completion",
|
|
84
|
+
"checks": [
|
|
85
|
+
{
|
|
86
|
+
"kind": "minBytes",
|
|
87
|
+
"file": "reports/social-digest-latest.md",
|
|
88
|
+
"bytes": 400,
|
|
89
|
+
"artifact": true
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"kind": "contains",
|
|
93
|
+
"file": "reports/social-digest-latest.md",
|
|
94
|
+
"pattern": "```gezel-action",
|
|
95
|
+
"label": "digest carries at least one one-click action",
|
|
96
|
+
"artifact": true
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"kind": "judge",
|
|
100
|
+
"file": "reports/social-digest-latest.md",
|
|
101
|
+
"rubric": "Every count or metric named in the digest appears verbatim in the scan notes; mentions name real authors from the notes; nothing is invented.",
|
|
102
|
+
"sourceFiles": [
|
|
103
|
+
"reports/social-digest-notes.md"
|
|
104
|
+
],
|
|
105
|
+
"label": "no invented metrics",
|
|
106
|
+
"artifact": true
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"onReject": "compose",
|
|
110
|
+
"maxAttempts": 4
|
|
111
|
+
},
|
|
112
|
+
"next": "evaluate"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "evaluate",
|
|
116
|
+
"name": "Evaluate",
|
|
117
|
+
"description": "Grade the digest against every criterion. All pass — finish; any fail — loop back to compose with the named gaps.",
|
|
118
|
+
"suggestedRole": "reviewer",
|
|
119
|
+
"prompt": "Open `reports/social-digest-latest.md` and `reports/social-digest-notes.md` with `read_artifact` and grade the digest. Check EACH criterion and write PASS/FAIL with a one-line reason:\n\n1. Every number in the digest appears in the notes (spot-check at least three).\n2. Each feed present in the notes is covered — or its silence is stated in one line.\n3. Mentions name their authors and quote them accurately.\n4. The engagement-movers section compares real counts from the notes, not impressions.\n5. There are 1-3 suggested next moves, and every ```gezel-action block is valid flat YAML with `kind`, `id`, `title`, `reason`, and — for fire-craftbook — `craftbookId: draft-social-post` only.\n6. The digest is tight enough to read over coffee: no padding, no repeated sections.\n\nThen route — this is the whole point of the loop:\n\n- **Every criterion PASSES →** call `advance_task_step({ ref, stepId: \"evaluate\", next: \"finish\" })`.\n- **Any criterion FAILS →** write the specific gaps to task notes with `write_task_note`, then call `advance_task_step({ ref, stepId: \"evaluate\", next: \"compose\" })` to loop back. The compose step fixes exactly those gaps.\n\nNever route to finish while a criterion is unmet. After ~3 unproductive loops, stop and report DONE_WITH_CONCERNS so the user can step in.",
|
|
120
|
+
"next": "compose"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "finish",
|
|
124
|
+
"name": "Finish",
|
|
125
|
+
"description": "Stamp the summary note and stop.",
|
|
126
|
+
"suggestedRole": "omroeper",
|
|
127
|
+
"prompt": "Write a one-paragraph DONE summary with `write_task_note`: the digest artifact paths (the dated file and `reports/social-digest-latest.md`), the headline in one line, how many mentions deserve a reply, and how many one-click actions the digest carries. Report DONE.",
|
|
128
|
+
"terminal": true
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
"version": "1.0.0",
|
|
132
|
+
"releasedAt": "2026-08-12T00:00:00Z"
|
|
133
|
+
}
|