@officexapp/vidfarm-devcli 0.21.62 → 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.
- package/.agents/skills/dollarplatoon-skill/SKILL.md +163 -1174
- package/.agents/skills/dollarplatoon-skill/SOURCE.md +62 -0
- package/.agents/skills/dollarplatoon-skill/skill/clients.md +234 -0
- package/.agents/skills/dollarplatoon-skill/skill/feeds.md +326 -0
- package/.agents/skills/dollarplatoon-skill/skill/gigs.md +395 -0
- package/.agents/skills/dollarplatoon-skill/skill/gigworkers.md +324 -0
- package/.agents/skills/dollarplatoon-skill/skill/orders.md +573 -0
- package/.agents/skills/dollarplatoon-skill/skill/payouts.md +234 -0
- package/.agents/skills/dollarplatoon-skill/skill/platform.md +174 -0
- package/.agents/skills/dollarplatoon-skill/skill/prices.md +75 -0
- package/.agents/skills/dollarplatoon-skill/skill/pricing-and-tags.md +255 -0
- package/.agents/skills/dollarplatoon-skill/skill/proofs.md +555 -0
- package/.agents/skills/dollarplatoon-skill/skill/queue.md +404 -0
- package/.agents/skills/dollarplatoon-skill/skill/quickstart.md +191 -0
- package/.agents/skills/dollarplatoon-skill/skill/staging.md +178 -0
- package/.agents/skills/dollarplatoon-skill/skill/tasks.md +588 -0
- package/.agents/skills/dollarplatoon-skill/skill/web-pages.md +586 -0
- package/.agents/skills/vidfarm/SKILL.md +3 -3
- package/.agents/skills/vidfarm/references/core-workflows.md +39 -0
- package/SKILL.director.md +42 -3
- package/SKILL.md +3 -1
- package/clipper.md +20 -0
- package/dist/src/cli.js +50 -6
- package/dist/src/devcli/delivery-seal.js +119 -0
- package/dist/src/devcli/marketplace-console.js +1253 -0
- package/dist/src/devcli/marketplace-gigs.js +162 -16
- package/marketplace.md +275 -1
- package/package.json +24 -3
|
@@ -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.
|