@officexapp/vidfarm-devcli 0.21.61 → 0.21.63

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 (29) hide show
  1. package/.agents/skills/dollarplatoon-skill/SKILL.md +163 -1174
  2. package/.agents/skills/dollarplatoon-skill/SOURCE.md +62 -0
  3. package/.agents/skills/dollarplatoon-skill/skill/clients.md +234 -0
  4. package/.agents/skills/dollarplatoon-skill/skill/feeds.md +326 -0
  5. package/.agents/skills/dollarplatoon-skill/skill/gigs.md +395 -0
  6. package/.agents/skills/dollarplatoon-skill/skill/gigworkers.md +324 -0
  7. package/.agents/skills/dollarplatoon-skill/skill/orders.md +573 -0
  8. package/.agents/skills/dollarplatoon-skill/skill/payouts.md +234 -0
  9. package/.agents/skills/dollarplatoon-skill/skill/platform.md +174 -0
  10. package/.agents/skills/dollarplatoon-skill/skill/prices.md +75 -0
  11. package/.agents/skills/dollarplatoon-skill/skill/pricing-and-tags.md +255 -0
  12. package/.agents/skills/dollarplatoon-skill/skill/proofs.md +555 -0
  13. package/.agents/skills/dollarplatoon-skill/skill/queue.md +404 -0
  14. package/.agents/skills/dollarplatoon-skill/skill/quickstart.md +191 -0
  15. package/.agents/skills/dollarplatoon-skill/skill/staging.md +178 -0
  16. package/.agents/skills/dollarplatoon-skill/skill/tasks.md +588 -0
  17. package/.agents/skills/dollarplatoon-skill/skill/web-pages.md +586 -0
  18. package/.agents/skills/vidfarm/SKILL.md +3 -3
  19. package/.agents/skills/vidfarm/references/core-workflows.md +39 -0
  20. package/SKILL.director.md +42 -3
  21. package/SKILL.md +3 -1
  22. package/clipper.md +20 -0
  23. package/dist/src/cli.js +50 -6
  24. package/dist/src/devcli/delivery-seal.js +119 -0
  25. package/dist/src/devcli/marketplace-console.js +1253 -0
  26. package/dist/src/devcli/marketplace-gigs.js +162 -16
  27. package/marketplace.md +275 -1
  28. package/package.json +28 -3
  29. package/public/assets/homepage-client-app.js +13 -13
@@ -0,0 +1,255 @@
1
+ # Per-task pricing, tags, and filters
2
+
3
+ How one gig carries work of different shapes and different values — and how a worker takes only
4
+ the part they want.
5
+
6
+ ## Contents
7
+
8
+ - Per-task pricing: the three states
9
+ - Setting a price
10
+ - How a price becomes a payout
11
+ - Task tags
12
+ - The matching rule (the part people get wrong)
13
+ - `default_task_tags` — required for email gigs
14
+ - Filtering when you poll
15
+ - Filtering what is pushed to you
16
+ - TBD prices and filters
17
+ - Tags on proofs
18
+ - Tag query parameters, everywhere
19
+
20
+ ---
21
+
22
+ ## Per-task pricing: the three states
23
+
24
+ A gig has a `price`, and by default every task in it is worth exactly that. A client who needs
25
+ variable pay sets a price on the task itself. Every task is in exactly one of three states:
26
+
27
+ | State | How | What the worker sees | What it pays |
28
+ |---|---|---|---|
29
+ | Gig price (default) | send nothing | the gig price | `gig.price` at proof time |
30
+ | Fixed | `?price=2.50` or `PATCH .../price` | `$2.50` | `$2.50` |
31
+ | TBD | `?price=tbd` | `TBD` | decided at approval; the gig price if never set |
32
+
33
+ **`TBD` is not `$0`.** An unpriced TBD settles at the gig price if the client never names an
34
+ amount. Never render it as zero.
35
+
36
+ ## Setting a price
37
+
38
+ **1. At submission**, on the publisher webhook. The body is the task payload, so the price rides
39
+ the query string — same convention as `priority` and `tags`:
40
+
41
+ ```
42
+ POST /inbound/webhook/GIG_abc?token=...&price=2.50
43
+ POST /inbound/webhook/GIG_abc?token=...&price=tbd
44
+ ```
45
+
46
+ Works on queue and push gigs alike.
47
+
48
+ **Email tasks always land at the gig price**, because an inbound email has nowhere to carry a
49
+ price. Reprice afterwards.
50
+
51
+ **2. One task at a time:**
52
+
53
+ ```json
54
+ PATCH /gigs/:id/tasks/:msgId/price
55
+ { "price": 2.50 } // fix the amount
56
+ { "price": "tbd" } // decide later
57
+ { "price": null } // clear the override — back to the gig price
58
+
59
+ → { "success": true, "id": "TASK_...", "price": 2.5, "price_tbd": false,
60
+ "price_source": "task", "proof_updated": false }
61
+ ```
62
+
63
+ `proof_updated: true` means a TBD proof was already waiting on this number and has now been given
64
+ it — you do not have to name the same amount again at approval.
65
+
66
+ Returns `409` if the task's proof has already locked a price. **That lock is the point:** a worker
67
+ agreed to an amount when they submitted, and review cannot quietly lower it.
68
+
69
+ **3. In bulk** — up to 100 per call, for splitting a queue into pay bands:
70
+
71
+ ```json
72
+ PATCH /gigs/:id/queue/prices
73
+ { "updates": [ { "id": "TASK_a", "price": 5.00 }, { "id": "TASK_b", "price": "tbd" } ] }
74
+
75
+ → { "success": true, "updated": 1,
76
+ "applied": [ { "id": "TASK_a", "price": 5 } ],
77
+ "skipped": [ { "id": "TASK_b", "reason": "proof_price_locked" } ] }
78
+ ```
79
+
80
+ The whole batch is validated before any of it is written, so a malformed entry returns `400` and
81
+ changes nothing. Unlike the priority equivalent this **accepts claimed tasks**, because a TBD is
82
+ normally priced after a worker has taken it. Individual failures come back in `skipped`:
83
+ `not_found` or `proof_price_locked`.
84
+
85
+ **4. At approval**, for a TBD task:
86
+
87
+ ```json
88
+ PATCH /gigs/:id/proofs/:proof_id { "action": "approve", "amount": 7.50 }
89
+ ```
90
+
91
+ `amount` is accepted only while `locked_price` is still `null`. Sending it for an already-priced
92
+ proof returns `409`. Approving a TBD **without** `amount` pays the gig price.
93
+
94
+ ## How a price becomes a payout
95
+
96
+ ```
97
+ task price (or the gig price) → proof.locked_price at submission → rollup gross_amount
98
+ ```
99
+
100
+ The rule that keeps this safe: **anything unresolvable falls back to the gig price.** A proof with
101
+ no matching task, a task naming another worker's mailbox, a row written before per-task pricing
102
+ existed, and a TBD nobody ever priced all pay `gig.price`. No task can pay `$0` by accident, and
103
+ none can be stranded unpaid.
104
+
105
+ A task's price only counts when the task belongs to the submitting mailbox. Naming another
106
+ worker's expensive task id in `task_identifier` does not buy you their rate — it silently resolves
107
+ to the gig price.
108
+
109
+ ## Task tags
110
+
111
+ A gig is one vending machine. Tags let it carry related but different work — `shortform`,
112
+ `longform`, `thumbnail` — so a worker takes only the shapes they want, instead of you opening a
113
+ separate gig for each.
114
+
115
+ At submission:
116
+
117
+ ```
118
+ POST /inbound/webhook/GIG_abc?token=...&tags=shortform,urgent
119
+ ```
120
+
121
+ Afterwards:
122
+
123
+ ```json
124
+ PATCH /gigs/:id/tasks/:msgId/tags { "tags": ["shortform"] } // null clears
125
+ PATCH /gigs/:id/queue/tags { "updates": [ { "id": "TASK_a", "tags": ["shortform"] } ] }
126
+ ```
127
+
128
+ Up to 100 tasks per bulk call, 25 tags per task, 256 characters each, matching always
129
+ case-insensitive. Retagging does **not** re-run distribution — a push task already delivered stays
130
+ where it is.
131
+
132
+ **To confirm what a task actually carries, call `GET /gigs/:id/queue`.** Each item returns its
133
+ `tags` and `price`, plus `price_source` (`"task"`, `"gig"`, or `"tbd"`). Do not use `/queue/poll`
134
+ to check — it is worker-only and it claims what it returns.
135
+
136
+ ## The matching rule (the part people get wrong)
137
+
138
+ **A tag filter matches only tasks carrying a matching tag.** Filtering for `category_a` returns
139
+ `category_a` work and nothing else. **An untagged task does not match — it is not a wildcard.**
140
+
141
+ Tags and price are independent. Filtering on price alone does not require the task to have tags at
142
+ all.
143
+
144
+ ## `default_task_tags` — required for email gigs
145
+
146
+ An inbound email has nowhere to carry `?tags=`. So on an email gig, a worker with any tag filter
147
+ receives **nothing, forever**.
148
+
149
+ ```json
150
+ PATCH /gigs/:id { "default_task_tags": ["shortform"] }
151
+ ```
152
+
153
+ It is stamped on any task that arrives without tags of its own — every email task, and any webhook
154
+ call that omitted `?tags=`. The matching rule is unchanged; the gig just supplies a tag when the
155
+ publisher cannot.
156
+
157
+ ## Filtering when you poll
158
+
159
+ ```json
160
+ POST /gigs/:id/queue/poll
161
+ {
162
+ "count": 5,
163
+ "tags": ["shortform", "thumbnail"], // OR'd; also accepts "a,b" as a string
164
+ "tag_match": "substring", // substring (default) | prefix | exact
165
+ "price_min": 2.00,
166
+ "price_max": 50.00,
167
+ "accept_tbd": true // default true
168
+ }
169
+ ```
170
+
171
+ **An empty filtered poll does not mean "no work".** Matching happens after rows are read, over the
172
+ first 1000 queue rows, so a narrow filter on a deep queue can run out of scan budget:
173
+
174
+ ```json
175
+ { "tasks": [], "count": 0, "scan_exhausted": true, "filter_applied": true }
176
+ ```
177
+
178
+ - `scan_exhausted: false` — genuinely nothing matches. Back off, or widen the filter.
179
+ - `scan_exhausted: true` — the scan ran out of budget. **Poll again**, or widen the filter.
180
+
181
+ Both `queue` and `queue_solo` report this.
182
+
183
+ ## Filtering what is pushed to you
184
+
185
+ On a push gig you do not poll — the client sends work to you. Store a standing preference on your
186
+ mailbox instead:
187
+
188
+ ```json
189
+ PATCH /gigs/:id/mailboxes/:mbx_id
190
+ {
191
+ "filter_tags": ["shortform"], // [] or null = accept every shape
192
+ "filter_tag_match": "substring",
193
+ "filter_price_min": 2.00,
194
+ "filter_price_max": null,
195
+ "filter_accept_tbd": true
196
+ }
197
+ ```
198
+
199
+ Worker-only: the gig owner can neither set these nor read them back. Same matching rule as
200
+ polling. A mailbox with no filters accepts everything — which is every mailbox that existed before
201
+ this feature, so **nothing changes until you opt in**.
202
+
203
+ If a task matches nobody it is dropped, and the publisher is told with `no_matching_mailboxes` —
204
+ see [tasks.md](https://dollarplatoon.com/skill/tasks.md).
205
+
206
+ **Owners: publish your tag vocabulary in `terms`.** Workers set filters *before* they have seen a
207
+ single task, and `terms` is the one field they read before joining. A worker who guesses `short`
208
+ when you send `shortform-vertical` silently receives less work, with no error on either side.
209
+
210
+ ## TBD prices and filters
211
+
212
+ A `price_tbd` task has no price yet, so a price range cannot honestly include or exclude it. It
213
+ **bypasses** the price test unless you set `accept_tbd: false`.
214
+
215
+ `gig.price` is deliberately not treated as a floor here: an owner can price a TBD below it at
216
+ approval, so pretending to know the amount would make a promise the payout does not keep. Tag
217
+ filters still apply.
218
+
219
+ ## Tags on proofs
220
+
221
+ Proofs carry `tags` too, so a long history stays organised on both sides.
222
+
223
+ - Set them at submission: `POST /gigs/:id/proofs` (and the public share-token submit) accept
224
+ `"tags": ["revision", "batch_7"]`.
225
+ - Change them later: `PATCH /gigs/:id/proofs/:proof_id/tags` `{ "tags": [...] }`. An empty array
226
+ clears them.
227
+ - **Both the gig owner and the worker who submitted may edit them, and both see the same list** —
228
+ unlike an alias, which is private per viewer. That shared visibility is what makes proof tags
229
+ usable as a filter the two sides agree on.
230
+
231
+ ## Tag query parameters, everywhere
232
+
233
+ These four params behave identically on every route that accepts them:
234
+
235
+ | Param | Meaning |
236
+ |---|---|
237
+ | `?tag=` | Comma-separated terms. Always case-insensitive. |
238
+ | `?tag_match=` | How each term compares: `substring` (default), `prefix` (alias `starts_with`), `exact`. Anything unrecognised falls back to `substring`. |
239
+ | `?tag_mode=` | How several terms combine: `any` (default, OR) or `all` (AND). |
240
+ | `?q=` | Plain text search over the row's text fields. |
241
+
242
+ They work on `GET /work/available`, `GET /mailboxes/mine`, `GET /gigs/mine`,
243
+ `GET /gigs/:id/queue`, `GET /gigs/:id/dashboard/inbound`, and both feed list routes.
244
+
245
+ **Why filtering behaves the way it does.** Tags cannot be indexed in the underlying datastore, and
246
+ there is no tag index anywhere on this platform. Every tag filter is matched in memory *after* a
247
+ page has been read, and it is only affordable because the partition — one gig, one feed, one user
248
+ — already bounds that read.
249
+
250
+ Two consequences you must handle:
251
+
252
+ 1. **A filtered page can be shorter than `limit` while `next_cursor` is still set.** Page until
253
+ `next_cursor` is `null`.
254
+ 2. **There is no cross-gig tag search.** Filtering is always scoped to something you already
255
+ named. That is a deliberate limit, not a missing feature.