@rightkit/social-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +40 -2
  5. package/dist/appName.d.ts +2 -0
  6. package/dist/appName.js +8 -0
  7. package/dist/components/ScopeSelect.d.ts +7 -0
  8. package/dist/components/ScopeSelect.js +6 -0
  9. package/dist/components/Shared.d.ts +34 -0
  10. package/dist/components/Shared.js +55 -0
  11. package/dist/data/derive.d.ts +21 -0
  12. package/dist/data/derive.js +47 -0
  13. package/dist/data/draftRules.d.ts +10 -0
  14. package/dist/data/draftRules.js +20 -0
  15. package/dist/data/engine/engineDataSource.d.ts +194 -0
  16. package/dist/data/engine/engineDataSource.js +836 -0
  17. package/dist/data/engine/errors.d.ts +13 -0
  18. package/dist/data/engine/errors.js +21 -0
  19. package/dist/data/engine/mapping.d.ts +172 -0
  20. package/dist/data/engine/mapping.js +1487 -0
  21. package/dist/data/engine/protocol.d.ts +910 -0
  22. package/dist/data/engine/protocol.js +67 -0
  23. package/dist/data/index.d.ts +18 -0
  24. package/dist/data/index.js +23 -0
  25. package/dist/data/mock/devSource.d.ts +7 -0
  26. package/dist/data/mock/devSource.js +10 -0
  27. package/dist/data/mock/fixtures-analytics.d.ts +7 -0
  28. package/dist/data/mock/fixtures-analytics.js +123 -0
  29. package/dist/data/mock/fixtures-briefs.d.ts +4 -0
  30. package/dist/data/mock/fixtures-briefs.js +230 -0
  31. package/dist/data/mock/fixtures-content.d.ts +4 -0
  32. package/dist/data/mock/fixtures-content.js +89 -0
  33. package/dist/data/mock/fixtures-digests.d.ts +3 -0
  34. package/dist/data/mock/fixtures-digests.js +40 -0
  35. package/dist/data/mock/fixtures-drafts.d.ts +2 -0
  36. package/dist/data/mock/fixtures-drafts.js +74 -0
  37. package/dist/data/mock/fixtures-gate.d.ts +3 -0
  38. package/dist/data/mock/fixtures-gate.js +49 -0
  39. package/dist/data/mock/fixtures-hooks.d.ts +2 -0
  40. package/dist/data/mock/fixtures-hooks.js +103 -0
  41. package/dist/data/mock/fixtures-inbox.d.ts +11 -0
  42. package/dist/data/mock/fixtures-inbox.js +213 -0
  43. package/dist/data/mock/fixtures-links.d.ts +5 -0
  44. package/dist/data/mock/fixtures-links.js +89 -0
  45. package/dist/data/mock/fixtures-memory.d.ts +3 -0
  46. package/dist/data/mock/fixtures-memory.js +148 -0
  47. package/dist/data/mock/fixtures-ops.d.ts +15 -0
  48. package/dist/data/mock/fixtures-ops.js +403 -0
  49. package/dist/data/mock/fixtures-optimise.d.ts +18 -0
  50. package/dist/data/mock/fixtures-optimise.js +221 -0
  51. package/dist/data/mock/fixtures-pacing.d.ts +5 -0
  52. package/dist/data/mock/fixtures-pacing.js +285 -0
  53. package/dist/data/mock/fixtures-publish.d.ts +2 -0
  54. package/dist/data/mock/fixtures-publish.js +129 -0
  55. package/dist/data/mock/fixtures-store.d.ts +10 -0
  56. package/dist/data/mock/fixtures-store.js +13 -0
  57. package/dist/data/mock/fixtures-variants.d.ts +5 -0
  58. package/dist/data/mock/fixtures-variants.js +138 -0
  59. package/dist/data/mock/fixtures.d.ts +6 -0
  60. package/dist/data/mock/fixtures.js +420 -0
  61. package/dist/data/mock/mockDataSource.d.ts +98 -0
  62. package/dist/data/mock/mockDataSource.js +903 -0
  63. package/dist/data/money.d.ts +14 -0
  64. package/dist/data/money.js +36 -0
  65. package/dist/data/platforms.d.ts +12 -0
  66. package/dist/data/platforms.js +26 -0
  67. package/dist/data/source.d.ts +196 -0
  68. package/dist/data/source.js +1 -0
  69. package/dist/data/time.d.ts +23 -0
  70. package/dist/data/time.js +86 -0
  71. package/dist/data/types.d.ts +1092 -0
  72. package/dist/data/types.js +3 -0
  73. package/dist/env.d.ts +59 -0
  74. package/dist/hooks/useAsync.d.ts +19 -0
  75. package/dist/hooks/useAsync.js +25 -0
  76. package/dist/index.d.ts +56 -0
  77. package/dist/index.js +58 -0
  78. package/dist/lib/host.d.ts +21 -0
  79. package/dist/lib/host.js +32 -0
  80. package/dist/lib/shell.d.ts +156 -0
  81. package/dist/lib/shell.js +16 -0
  82. package/dist/screens/AccountsScreen.d.ts +8 -0
  83. package/dist/screens/AccountsScreen.js +100 -0
  84. package/dist/screens/AnalyticsScreen.d.ts +7 -0
  85. package/dist/screens/AnalyticsScreen.js +64 -0
  86. package/dist/screens/ApprovalsScreen.d.ts +10 -0
  87. package/dist/screens/ApprovalsScreen.js +144 -0
  88. package/dist/screens/BriefsScreen.d.ts +7 -0
  89. package/dist/screens/BriefsScreen.js +83 -0
  90. package/dist/screens/CalendarScreen.d.ts +10 -0
  91. package/dist/screens/CalendarScreen.js +143 -0
  92. package/dist/screens/CampaignsScreen.d.ts +7 -0
  93. package/dist/screens/CampaignsScreen.js +52 -0
  94. package/dist/screens/ChannelMemoryScreen.d.ts +7 -0
  95. package/dist/screens/ChannelMemoryScreen.js +109 -0
  96. package/dist/screens/ContentScreen.d.ts +7 -0
  97. package/dist/screens/ContentScreen.js +64 -0
  98. package/dist/screens/DigestsScreen.d.ts +7 -0
  99. package/dist/screens/DigestsScreen.js +53 -0
  100. package/dist/screens/DraftsScreen.d.ts +10 -0
  101. package/dist/screens/DraftsScreen.js +108 -0
  102. package/dist/screens/FailuresScreen.d.ts +8 -0
  103. package/dist/screens/FailuresScreen.js +111 -0
  104. package/dist/screens/HookTestsScreen.d.ts +7 -0
  105. package/dist/screens/HookTestsScreen.js +54 -0
  106. package/dist/screens/InboxScreen.d.ts +12 -0
  107. package/dist/screens/InboxScreen.js +76 -0
  108. package/dist/screens/LinksScreen.d.ts +7 -0
  109. package/dist/screens/LinksScreen.js +52 -0
  110. package/dist/screens/PacingScreen.d.ts +7 -0
  111. package/dist/screens/PacingScreen.js +88 -0
  112. package/dist/screens/PublishJobsScreen.d.ts +7 -0
  113. package/dist/screens/PublishJobsScreen.js +66 -0
  114. package/dist/screens/SuggestionsScreen.d.ts +8 -0
  115. package/dist/screens/SuggestionsScreen.js +94 -0
  116. package/dist/screens/TextGateScreen.d.ts +7 -0
  117. package/dist/screens/TextGateScreen.js +111 -0
  118. package/dist/screens/ThreadsScreen.d.ts +8 -0
  119. package/dist/screens/ThreadsScreen.js +113 -0
  120. package/dist/screens/TypedBriefsScreen.d.ts +7 -0
  121. package/dist/screens/TypedBriefsScreen.js +75 -0
  122. package/dist/screens/labels.d.ts +121 -0
  123. package/dist/screens/labels.js +272 -0
  124. package/package.json +56 -3
@@ -0,0 +1,148 @@
1
+ import { SCOPE_ALPHA } from "./fixtures-optimise.js";
2
+ const SCOPE = { venture: "fixture-alpha", channel: "alpha-shorts", account: "alpha-main" };
3
+ export const FIXTURE_MEMORY = [
4
+ {
5
+ // First revision: no cadence and no banned moves yet, so the screen says those sections are absent.
6
+ scopeId: SCOPE_ALPHA,
7
+ revision: 1,
8
+ supersedes: null,
9
+ author: "adrian",
10
+ createdAtUtc: "2026-09-01T09:00:00Z",
11
+ contentHash: "fnv1a128:1f0e2d3c4b5a69788796a5b4c3d2e1f0",
12
+ document: {
13
+ contract: "channel-memory",
14
+ contractVersion: "1.0.0",
15
+ memoryId: "fixture-memory-alpha",
16
+ scope: SCOPE,
17
+ brandCode: "fixture-brand",
18
+ voice: {
19
+ tone: "plain and warm",
20
+ audience: "fixture viewers aged 8 to 12 and their parents",
21
+ narration: "first person, one speaker",
22
+ },
23
+ pillars: [
24
+ { name: "Fixture pillar: puddle science", description: "Short experiments done outdoors." },
25
+ { name: "Fixture pillar: bedtime routines", description: "Calm endings for the day." },
26
+ ],
27
+ },
28
+ },
29
+ {
30
+ // Latest revision: adds cadence and banned moves, and tightens the voice.
31
+ scopeId: SCOPE_ALPHA,
32
+ revision: 2,
33
+ supersedes: 1,
34
+ author: "adrian",
35
+ createdAtUtc: "2026-10-06T10:30:00Z",
36
+ contentHash: "fnv1a128:9a8b7c6d5e4f30211f2e3d4c5b6a7980",
37
+ document: {
38
+ contract: "channel-memory",
39
+ contractVersion: "1.0.0",
40
+ memoryId: "fixture-memory-alpha",
41
+ scope: SCOPE,
42
+ brandCode: "fixture-brand",
43
+ voice: {
44
+ tone: "plain, warm and brief",
45
+ audience: "fixture viewers aged 8 to 12 and their parents",
46
+ narration: "first person, one speaker, no questions to the viewer",
47
+ personaNotes: "Fixture note: keep every sentence under twelve words.",
48
+ },
49
+ pillars: [
50
+ { name: "Fixture pillar: puddle science", description: "Short experiments done outdoors." },
51
+ { name: "Fixture pillar: bedtime routines", description: "Calm endings for the day." },
52
+ { name: "Fixture pillar: fixture crafts", description: "Paper and tape projects." },
53
+ ],
54
+ cadence: {
55
+ postsPerDay: 1,
56
+ timezone: "Europe/Lisbon",
57
+ batch: "weekly",
58
+ batchWindow: "Sunday 18:00 to 20:00",
59
+ },
60
+ bannedMoves: [
61
+ {
62
+ id: "fixture-no-guarantees",
63
+ appliesTo: "any",
64
+ description: "No promise of results.",
65
+ phrases: ["guarantee", "guaranteed"],
66
+ },
67
+ ],
68
+ bannedPhrases: ["guarantee", "guaranteed"],
69
+ },
70
+ },
71
+ ];
72
+ export const FIXTURE_PROOFS = [
73
+ {
74
+ // Approved: a script can cite it.
75
+ scopeId: SCOPE_ALPHA,
76
+ answerId: "ans.alpha-01",
77
+ proofId: "proof.alpha.episodes-published",
78
+ status: "approved",
79
+ subject: "subject.episodes-published",
80
+ value: "96",
81
+ units: "episodes",
82
+ origin: "interview",
83
+ observedFrom: "2026-01-05",
84
+ observedTo: "2026-10-01",
85
+ supersededBy: null,
86
+ withdrawalNote: null,
87
+ },
88
+ {
89
+ // Approved, with a decimal value kept exactly as stored.
90
+ scopeId: SCOPE_ALPHA,
91
+ answerId: "ans.alpha-02",
92
+ proofId: "proof.alpha.average-length",
93
+ status: "approved",
94
+ subject: "subject.average-length",
95
+ value: "14.5",
96
+ units: "minutes",
97
+ origin: "interview",
98
+ observedFrom: "2026-06-01",
99
+ observedTo: "2026-09-30",
100
+ supersededBy: null,
101
+ withdrawalNote: null,
102
+ },
103
+ {
104
+ // Collected: not approved, so no script can cite it yet.
105
+ scopeId: SCOPE_ALPHA,
106
+ answerId: "ans.alpha-03",
107
+ proofId: "proof.alpha.shorts-per-week",
108
+ status: "collected",
109
+ subject: "subject.shorts-per-week",
110
+ value: "3",
111
+ units: "shorts",
112
+ origin: "interview",
113
+ observedFrom: "2026-10-01",
114
+ observedTo: "",
115
+ supersededBy: null,
116
+ withdrawalNote: null,
117
+ },
118
+ {
119
+ // Superseded: replaced by the approved proof named here.
120
+ scopeId: SCOPE_ALPHA,
121
+ answerId: "ans.alpha-04",
122
+ proofId: "proof.alpha.episodes-published-v1",
123
+ status: "superseded",
124
+ subject: "subject.episodes-published",
125
+ value: "91",
126
+ units: "episodes",
127
+ origin: "interview",
128
+ observedFrom: "2025-12-01",
129
+ observedTo: "2026-06-30",
130
+ supersededBy: "proof.alpha.episodes-published",
131
+ withdrawalNote: null,
132
+ },
133
+ {
134
+ // Withdrawn: the note the engine holds is shown.
135
+ scopeId: SCOPE_ALPHA,
136
+ answerId: "ans.alpha-05",
137
+ proofId: "proof.alpha.studio-hours",
138
+ status: "withdrawn",
139
+ subject: "subject.studio-hours",
140
+ value: "40",
141
+ units: "hours",
142
+ origin: "interview",
143
+ observedFrom: "2026-03-01",
144
+ observedTo: "2026-03-31",
145
+ supersededBy: null,
146
+ withdrawalNote: "Fixture note: the answer was withdrawn by the owner.",
147
+ },
148
+ ];
@@ -0,0 +1,15 @@
1
+ import type { FailureItem, ModerationCapability, PublishJob, ReconcileResult, ThreadItem } from "../types.js";
2
+ /** Two more jobs that the failures screen needs: a dead letter and a second unknown outcome. */
3
+ export declare const EXTRA_JOBS: PublishJob[];
4
+ /**
5
+ * What the platform reports when the operator checks an outcome_unknown job. Fixed per job so the
6
+ * mock is deterministic: job-1007 was accepted by the platform; job-1014 was not.
7
+ */
8
+ export declare const RECONCILE_RESULTS: Record<string, (checkedAtUtc: string) => ReconcileResult>;
9
+ export declare const FIXTURE_FAILURES: FailureItem[];
10
+ export declare const FIXTURE_THREADS: ThreadItem[];
11
+ /**
12
+ * Mock capability matrix. It is not a claim about any platform's live rules: each row must be
13
+ * confirmed per platform before live use (plan section 4, P1). The screen says so.
14
+ */
15
+ export declare const FIXTURE_MODERATION: ModerationCapability[];
@@ -0,0 +1,403 @@
1
+ const b3 = (hex) => `blake3:${hex}`;
2
+ /** Two more jobs that the failures screen needs: a dead letter and a second unknown outcome. */
3
+ export const EXTRA_JOBS = [
4
+ {
5
+ id: "job-1013",
6
+ accountId: "acc-pp-yt",
7
+ briefId: "brief-rain-cloud",
8
+ format: "short",
9
+ revision: 3,
10
+ caption: "Pebble tries to catch the rain cloud. It rains on Pip instead.",
11
+ captionDigest: "",
12
+ firstComment: null,
13
+ settings: ["Visibility: public", "Made for kids: yes"],
14
+ mediaItems: [
15
+ { hash: b3("5e0c9b1a7d2f3486"), kind: "video", durationSeconds: 33, dimensions: "1080x1920", status: "ready", uploadPercent: null },
16
+ ],
17
+ scheduledAtUtc: "2026-10-12T04:00:00Z",
18
+ timeZone: "Europe/Lisbon",
19
+ dst: { kind: "none" },
20
+ state: "failed",
21
+ approval: { status: "approved", approvedRevision: 3, policyRevision: 14, approvedAtUtc: "2026-10-11T18:00:00Z" },
22
+ policyRevision: 14,
23
+ lateness: null,
24
+ conflict: null,
25
+ },
26
+ {
27
+ id: "job-1014",
28
+ accountId: "acc-pp-ig-main",
29
+ briefId: "brief-puddle-rain",
30
+ format: "reel",
31
+ revision: 2,
32
+ caption: "Pip and the puddle rain cloud, all the way to the end. Episode 13 link in bio.",
33
+ captionDigest: "",
34
+ firstComment: null,
35
+ settings: ["Visibility: public", "Alt text: present", "AI label: off"],
36
+ mediaItems: [
37
+ { hash: b3("f04a8c2e91d7b635"), kind: "video", durationSeconds: 40, dimensions: "1080x1920", status: "ready", uploadPercent: null },
38
+ ],
39
+ scheduledAtUtc: "2026-10-11T15:00:00Z",
40
+ timeZone: "Europe/Lisbon",
41
+ dst: { kind: "none" },
42
+ state: "outcome_unknown",
43
+ approval: { status: "approved", approvedRevision: 2, policyRevision: 14, approvedAtUtc: "2026-10-10T12:00:00Z" },
44
+ policyRevision: 14,
45
+ lateness: null,
46
+ conflict: null,
47
+ },
48
+ {
49
+ // Waiting on the platform quota. The daemon resumes the job at publish.resumeAtUtc.
50
+ id: "job-1015",
51
+ accountId: "acc-pp-ig-shorts",
52
+ briefId: "brief-umbrella-reprise",
53
+ format: "reel",
54
+ revision: 2,
55
+ caption: "Pebble sings the umbrella song once more, in the rain. Full episode on the channel.",
56
+ captionDigest: "",
57
+ firstComment: null,
58
+ settings: ["Visibility: public", "Alt text: present", "AI label: off"],
59
+ mediaItems: [
60
+ { hash: b3("3a7d1c9e5f024b68"), kind: "video", durationSeconds: 29, dimensions: "1080x1920", status: "ready", uploadPercent: null },
61
+ ],
62
+ scheduledAtUtc: "2026-10-13T06:20:00Z",
63
+ timeZone: "Europe/Lisbon",
64
+ dst: { kind: "none" },
65
+ state: "scheduled",
66
+ approval: { status: "approved", approvedRevision: 2, policyRevision: 14, approvedAtUtc: "2026-10-11T09:00:00Z" },
67
+ policyRevision: 14,
68
+ lateness: null,
69
+ conflict: null,
70
+ publish: {
71
+ state: "waiting",
72
+ permalink: null,
73
+ resumeAtUtc: "2026-10-13T06:20:00Z",
74
+ errorCode: "rate_limited",
75
+ errorMessage: "The platform asked for a wait before the next request.",
76
+ },
77
+ },
78
+ {
79
+ // Published. The permalink is on a real host with an obviously fake path, for review only.
80
+ id: "job-1016",
81
+ accountId: "acc-pp-yt",
82
+ briefId: "brief-umbrella-intro",
83
+ format: "short",
84
+ revision: 1,
85
+ caption: "Pip and Pebble meet the umbrella. Fake mock post for the preview.",
86
+ captionDigest: "",
87
+ firstComment: null,
88
+ settings: ["Visibility: public", "Made for kids: yes"],
89
+ mediaItems: [
90
+ { hash: b3("c81e0d4a7b29f356"), kind: "video", durationSeconds: 31, dimensions: "1080x1920", status: "ready", uploadPercent: null },
91
+ ],
92
+ scheduledAtUtc: "2026-10-12T12:00:00Z",
93
+ timeZone: "Europe/Lisbon",
94
+ dst: { kind: "none" },
95
+ state: "published",
96
+ approval: { status: "approved", approvedRevision: 1, policyRevision: 14, approvedAtUtc: "2026-10-09T08:30:00Z" },
97
+ policyRevision: 14,
98
+ lateness: null,
99
+ conflict: null,
100
+ publish: {
101
+ state: "published",
102
+ permalink: "https://www.youtube.com/shorts/mock-not-a-real-post",
103
+ resumeAtUtc: null,
104
+ errorCode: null,
105
+ errorMessage: null,
106
+ },
107
+ },
108
+ ];
109
+ /**
110
+ * What the platform reports when the operator checks an outcome_unknown job. Fixed per job so the
111
+ * mock is deterministic: job-1007 was accepted by the platform; job-1014 was not.
112
+ */
113
+ export const RECONCILE_RESULTS = {
114
+ "job-1007": (checkedAtUtc) => ({
115
+ outcome: "found_on_platform",
116
+ remoteId: "17998412776531209",
117
+ permalink: "https://instagram.example/p/CwQ7hK2vLmA/",
118
+ checkedAtUtc,
119
+ }),
120
+ "job-1014": (checkedAtUtc) => ({ outcome: "not_found", checkedAtUtc }),
121
+ };
122
+ export const FIXTURE_FAILURES = [
123
+ {
124
+ id: "alert-disk",
125
+ kind: "alert",
126
+ alertKind: "disk_full",
127
+ accountId: null,
128
+ jobId: null,
129
+ summary: "The server disk is 96 percent full. Media uploads start failing above 100 percent.",
130
+ nextAction: "Free space on the server disk, then acknowledge this alert.",
131
+ atUtc: "2026-10-12T14:20:00Z",
132
+ attempts: null,
133
+ status: "open",
134
+ reconcile: null,
135
+ resolution: null,
136
+ resolutionNote: null,
137
+ },
138
+ {
139
+ id: "alert-cred-tw-ig",
140
+ kind: "alert",
141
+ alertKind: "credentials_expired",
142
+ accountId: "acc-tw-ig",
143
+ jobId: null,
144
+ summary: "The Instagram token for @tidewatertea failed to refresh. Publishing for this account is blocked.",
145
+ nextAction: "Reconnect the account through the provider sign-in. Nothing for @tidewatertea publishes until then.",
146
+ atUtc: "2026-10-11T06:02:00Z",
147
+ attempts: null,
148
+ status: "open",
149
+ reconcile: null,
150
+ resolution: null,
151
+ resolutionNote: null,
152
+ },
153
+ {
154
+ id: "alert-quota-shorts",
155
+ kind: "alert",
156
+ alertKind: "quota_cooldown",
157
+ accountId: "acc-pp-ig-shorts",
158
+ jobId: null,
159
+ summary: "Instagram returned a dated Retry-After. Posts for @pipandpebble_shorts wait for the cooldown to end.",
160
+ nextAction: "Nothing to do. Posts resume after the cooldown. Acknowledge the alert to clear it.",
161
+ atUtc: "2026-10-12T16:00:00Z",
162
+ attempts: null,
163
+ status: "open",
164
+ reconcile: null,
165
+ resolution: null,
166
+ resolutionNote: null,
167
+ },
168
+ {
169
+ id: "dl-job-1013",
170
+ kind: "dead_letter",
171
+ alertKind: null,
172
+ accountId: "acc-pp-yt",
173
+ jobId: "job-1013",
174
+ summary: "YouTube returned a server error on all five attempts. The short was not published.",
175
+ nextAction: "Mark it cancelled, not published, or accept it as lost with a note. Then rebuild it on the desktop.",
176
+ atUtc: "2026-10-12T04:40:00Z",
177
+ attempts: 5,
178
+ status: "open",
179
+ reconcile: null,
180
+ resolution: null,
181
+ resolutionNote: null,
182
+ },
183
+ {
184
+ id: "ou-job-1007",
185
+ kind: "outcome_unknown",
186
+ alertKind: null,
187
+ accountId: "acc-tw-ig",
188
+ jobId: "job-1007",
189
+ summary: "The platform may have accepted this post. Nothing was retried.",
190
+ nextAction: "Check the platform. Record the result, then resolve. No retry until the outcome is known.",
191
+ atUtc: "2026-10-12T09:00:00Z",
192
+ attempts: 1,
193
+ status: "open",
194
+ reconcile: null,
195
+ resolution: null,
196
+ resolutionNote: null,
197
+ },
198
+ {
199
+ id: "ou-job-1014",
200
+ kind: "outcome_unknown",
201
+ alertKind: null,
202
+ accountId: "acc-pp-ig-main",
203
+ jobId: "job-1014",
204
+ summary: "The platform may have accepted this post. Nothing was retried.",
205
+ nextAction: "Check the platform. Record the result, then resolve. No retry until the outcome is known.",
206
+ atUtc: "2026-10-11T15:00:00Z",
207
+ attempts: 1,
208
+ status: "open",
209
+ reconcile: null,
210
+ resolution: null,
211
+ resolutionNote: null,
212
+ },
213
+ ];
214
+ export const FIXTURE_THREADS = [
215
+ {
216
+ id: "th-yt-episode",
217
+ accountId: "acc-pp-yt",
218
+ postTitle: "Umbrella song short",
219
+ postPublishedUtc: "2026-10-12T17:00:00Z",
220
+ kind: "comment",
221
+ messages: [
222
+ {
223
+ id: "m-yt-1",
224
+ author: "@viewer_marigold",
225
+ fromOperator: false,
226
+ sentAtUtc: "2026-10-12T17:40:00Z",
227
+ text: "Could the snail song be a full episode? My kids want it on repeat.",
228
+ },
229
+ ],
230
+ replyWindow: null,
231
+ autoReply: {
232
+ ruleName: "Episode question",
233
+ matchedKeyword: "episode",
234
+ draftReply: "The snail song is in episode 14, Puddle Patrol. The link is pinned on the channel.",
235
+ state: "matched_not_sent",
236
+ unavailableReason: null,
237
+ },
238
+ status: "open",
239
+ snoozedUntilUtc: null,
240
+ },
241
+ {
242
+ id: "th-yt-thanks",
243
+ accountId: "acc-pp-yt",
244
+ postTitle: "Umbrella song short",
245
+ postPublishedUtc: "2026-10-12T17:00:00Z",
246
+ kind: "comment",
247
+ messages: [
248
+ { id: "m-yt-2", author: "@sunny_street", fromOperator: false, sentAtUtc: "2026-10-12T09:10:00Z", text: "Great song!" },
249
+ ],
250
+ replyWindow: null,
251
+ autoReply: null,
252
+ status: "resolved",
253
+ snoozedUntilUtc: null,
254
+ },
255
+ {
256
+ id: "th-ig-link-dm",
257
+ accountId: "acc-pp-ig-main",
258
+ postTitle: "Puddle Patrol episode 13 reel",
259
+ postPublishedUtc: "2026-10-06T08:00:00Z",
260
+ kind: "dm",
261
+ messages: [
262
+ {
263
+ id: "m-dm-1",
264
+ author: "@little_ferns_fan",
265
+ fromOperator: false,
266
+ sentAtUtc: "2026-10-12T10:00:00Z",
267
+ text: "LINK please, we missed episode 13.",
268
+ },
269
+ ],
270
+ replyWindow: { closesUtc: "2026-10-12T16:00:00Z" },
271
+ autoReply: {
272
+ ruleName: "Send episode link",
273
+ matchedKeyword: "LINK",
274
+ draftReply: "Here is episode 13: https://pipandpebble.example/puddle-patrol-13",
275
+ state: "unavailable",
276
+ unavailableReason: "The reply window closed on Mon 12 Oct 17:00 (Europe/Lisbon). Nothing is sent after it closes.",
277
+ },
278
+ status: "open",
279
+ snoozedUntilUtc: null,
280
+ },
281
+ {
282
+ id: "th-shorts-optout",
283
+ accountId: "acc-pp-ig-shorts",
284
+ postTitle: "Lighthouse Game short",
285
+ postPublishedUtc: "2026-10-09T16:00:00Z",
286
+ kind: "comment",
287
+ messages: [
288
+ {
289
+ id: "m-os-1",
290
+ author: "@quiet_otter_07",
291
+ fromOperator: false,
292
+ sentAtUtc: "2026-10-12T12:20:00Z",
293
+ text: "Send me the link please.",
294
+ },
295
+ ],
296
+ replyWindow: { closesUtc: "2026-10-19T12:20:00Z" },
297
+ autoReply: {
298
+ ruleName: "Link on request",
299
+ matchedKeyword: "link",
300
+ draftReply: "The Lighthouse Game rules are in the pinned comment, with the link.",
301
+ state: "unavailable",
302
+ unavailableReason: "This person sent STOP on Fri 10 Oct 2026. Automated replies to them are blocked on this account.",
303
+ },
304
+ status: "open",
305
+ snoozedUntilUtc: null,
306
+ },
307
+ {
308
+ id: "th-tw-ship-dm",
309
+ accountId: "acc-tw-ig",
310
+ postTitle: "Cold Brew Sampler photo",
311
+ postPublishedUtc: "2026-10-09T13:00:00Z",
312
+ kind: "dm",
313
+ messages: [
314
+ {
315
+ id: "m-tw-1",
316
+ author: "@brew_and_bramble",
317
+ fromOperator: false,
318
+ sentAtUtc: "2026-10-12T14:05:00Z",
319
+ text: "Do you ship to Portugal?",
320
+ },
321
+ ],
322
+ replyWindow: { closesUtc: "2026-10-14T14:05:00Z" },
323
+ autoReply: {
324
+ ruleName: "Shipping question",
325
+ matchedKeyword: "ship",
326
+ draftReply: "We ship within the US and Canada. The shipping times are on the page linked in bio.",
327
+ state: "matched_not_sent",
328
+ unavailableReason: null,
329
+ },
330
+ status: "open",
331
+ snoozedUntilUtc: null,
332
+ },
333
+ {
334
+ id: "th-li-tins",
335
+ accountId: "acc-tw-li",
336
+ postTitle: "Cold brew line update",
337
+ postPublishedUtc: "2026-10-05T13:00:00Z",
338
+ kind: "comment",
339
+ messages: [
340
+ {
341
+ id: "m-li-1",
342
+ author: "@tea_quality_check",
343
+ fromOperator: false,
344
+ sentAtUtc: "2026-10-11T15:00:00Z",
345
+ text: "Are the new tins safe to keep in the fridge after opening?",
346
+ },
347
+ ],
348
+ replyWindow: null,
349
+ autoReply: null,
350
+ status: "snoozed",
351
+ snoozedUntilUtc: "2026-10-13T09:00:00Z",
352
+ },
353
+ ];
354
+ /**
355
+ * Mock capability matrix. It is not a claim about any platform's live rules: each row must be
356
+ * confirmed per platform before live use (plan section 4, P1). The screen says so.
357
+ */
358
+ export const FIXTURE_MODERATION = [
359
+ { platform: "instagram", action: "hide_comment", supported: true, reason: "Supported in this app." },
360
+ { platform: "instagram", action: "delete_comment", supported: true, reason: "Supported in this app." },
361
+ {
362
+ platform: "instagram",
363
+ action: "block_author",
364
+ supported: false,
365
+ reason: "Blocking authors is not in this app's capability list for Instagram.",
366
+ },
367
+ {
368
+ platform: "youtube",
369
+ action: "hide_comment",
370
+ supported: false,
371
+ reason: "Hiding comments is not supported for YouTube in this app.",
372
+ },
373
+ {
374
+ platform: "youtube",
375
+ action: "delete_comment",
376
+ supported: true,
377
+ reason: "Supported in this app.",
378
+ },
379
+ {
380
+ platform: "youtube",
381
+ action: "block_author",
382
+ supported: false,
383
+ reason: "Blocking authors is not supported for YouTube in this app.",
384
+ },
385
+ {
386
+ platform: "linkedin",
387
+ action: "hide_comment",
388
+ supported: false,
389
+ reason: "Hiding comments is not supported for LinkedIn in this app.",
390
+ },
391
+ {
392
+ platform: "linkedin",
393
+ action: "delete_comment",
394
+ supported: true,
395
+ reason: "Supported for comments on your own posts.",
396
+ },
397
+ {
398
+ platform: "linkedin",
399
+ action: "block_author",
400
+ supported: false,
401
+ reason: "Blocking authors is not supported for LinkedIn in this app.",
402
+ },
403
+ ];
@@ -0,0 +1,18 @@
1
+ import type { OperatorScope, OptimiseProposal, SpendAllowance, SpendPrice, SpendReport } from "../types.js";
2
+ export declare const SCOPE_ALPHA = "fixture-alpha/alpha-shorts/alpha-main";
3
+ export declare const SCOPE_BETA = "fixture-beta/beta-studio/beta-main";
4
+ export declare const SCOPE_GAMMA = "fixture-gamma/gamma-news/gamma-main";
5
+ export declare const MOCK_MONTH = "2026-10";
6
+ export declare const FIXTURE_SCOPES: OperatorScope[];
7
+ export declare const FIXTURE_PROPOSALS: OptimiseProposal[];
8
+ export interface FixtureAllowance {
9
+ scopeId: string;
10
+ allowance: SpendAllowance;
11
+ }
12
+ export declare const FIXTURE_ALLOWANCES: FixtureAllowance[];
13
+ export interface FixtureReport {
14
+ scopeId: string;
15
+ report: SpendReport;
16
+ }
17
+ export declare const FIXTURE_REPORTS: FixtureReport[];
18
+ export declare const FIXTURE_PRICES: SpendPrice[];