@aotter/mantle 0.1.0-alpha.6 → 0.1.0-alpha.7

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.
@@ -177,3 +177,10 @@ Preserve source semantics:
177
177
  - animated GIFs must stay animated;
178
178
  - do not silently flatten, resize, or recompress user assets without
179
179
  asking.
180
+
181
+ ## Cleanup
182
+
183
+ Run the version-matched `media-gc` skill when an upload reached R2 but was
184
+ never committed. It audits first and removes only stale objects without
185
+ `committedAt` metadata after explicit operator confirmation. Do not use an R2
186
+ lifecycle rule: committed and uncommitted media share the same purpose prefix.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.7",
4
4
  "description": "Umbrella entry for @aotter/mantle. Adopters install this one package and import from subpaths: /spec, /runtime, /cloudflare, /admin-ui. Sub-packages remain individually installable on npm for tooling and adapter authors.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://mantle.tools/",
@@ -55,10 +55,10 @@
55
55
  "README.md"
56
56
  ],
57
57
  "dependencies": {
58
- "@aotter/mantle-admin-ui": "0.1.0-alpha.6",
59
- "@aotter/mantle-cloudflare": "0.1.0-alpha.6",
60
- "@aotter/mantle-spec": "0.1.0-alpha.6",
61
- "@aotter/mantle-runtime": "0.1.0-alpha.6"
58
+ "@aotter/mantle-admin-ui": "0.1.0-alpha.7",
59
+ "@aotter/mantle-cloudflare": "0.1.0-alpha.7",
60
+ "@aotter/mantle-runtime": "0.1.0-alpha.7",
61
+ "@aotter/mantle-spec": "0.1.0-alpha.7"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "@cloudflare/workers-oauth-provider": "^0.8.2",
package/skills/README.md CHANGED
@@ -5,6 +5,7 @@ Agent-readable skill briefs for consumers of `@aotter/mantle-*`. Discoverable by
5
5
  | Skill | When to invoke |
6
6
  |---|---|
7
7
  | [`develop`](develop/SKILL.md) | `mantle:develop`: Core-owned workflow for manifest, runtime, handler, adapter, validation, and MCP work in any Mantle project. |
8
+ | [`media-gc`](media-gc/SKILL.md) | `mantle:media-gc`: audit or remove stale uncommitted public media objects with the connected Cloudflare API. |
8
9
  | [`plugin`](plugin/SKILL.md) | `mantle:plugin`: Core-owned marketplace workflow for plan-first capability installs across starters and adapters. |
9
10
  | [`theme`](theme/SKILL.md) | `mantle:theme`: Core-owned visual workflow. Reads project context but does not depend on starter-owned skill semantics. |
10
11
  | [`update`](update/SKILL.md) | `mantle:update`: Core-owned drift check workflow for SDK, starter snapshots, and plugin lockfiles. |
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: media-gc
3
+ description: Audit and safely remove stale, uncommitted public media uploads from a Mantle Cloudflare R2 bucket. Use when a Mantle operator asks to inspect or clean orphan media objects left after create_media_upload without commit_media_upload.
4
+ ---
5
+
6
+ # Mantle Media GC
7
+
8
+ Use the connected Cloudflare API. Audit by default; delete only the exact
9
+ objects approved by the user.
10
+
11
+ ## Preflight
12
+
13
+ 1. Read the project's Wrangler config and Mantle config. Resolve the R2 bucket
14
+ binding, bucket name, and declared public media purpose names.
15
+ 2. Confirm the exact Cloudflare account and bucket. If either is ambiguous,
16
+ ask; never choose by name similarity.
17
+ 3. Use the Cloudflare OpenAPI search before execution to resolve the current
18
+ R2 List Objects and Delete Objects endpoints. If the connected account lacks
19
+ access, stop. Do not create, request, or store credentials.
20
+ 4. Stop when the project has no public R2 media binding or no declared purpose.
21
+
22
+ ## Audit
23
+
24
+ For each declared purpose, list up to 1,000 objects per page under the exact
25
+ `<purpose>/` prefix. Use the API cursor until `is_truncated` is false.
26
+
27
+ An object is a deletion candidate only when all conditions hold:
28
+
29
+ - `last_modified` is more than 24 hours old;
30
+ - `custom_metadata.committedAt` is absent or empty;
31
+ - the key matches the exact Mantle layout
32
+ `<purpose>/<group>/(primary|alternate|fallback).(png|jpg|webp|avif|gif|svg)`,
33
+ where `<purpose>` is declared by this project and `<group>` contains only
34
+ letters, digits, `_`, or `-`.
35
+
36
+ Skip committed, unknown-prefix, unprefixed, malformed, or ambiguous objects.
37
+ Delete only an uncommitted variant, never its whole group.
38
+
39
+ Report the account, bucket, purpose prefixes, UTC cutoff, candidate object and
40
+ group counts, total bytes, skipped count, and a SHA-256 digest of the sorted
41
+ `key + etag` candidate set. Do not print object keys, upload group IDs,
42
+ filenames, public URLs, signed URLs, or secrets.
43
+
44
+ ## Apply
45
+
46
+ 1. Show the audit summary and get explicit confirmation for that exact account,
47
+ bucket, cutoff, count, byte total, and candidate-set digest.
48
+ 2. Re-run the complete audit with the same UTC cutoff. If the candidate count,
49
+ bytes, or digest changed, stop and present the new audit for confirmation.
50
+ 3. Call Delete Objects with JSON arrays of exact keys, at most 1,000 keys per
51
+ request. Never send the `prefix` query parameter: an empty prefix can empty
52
+ the bucket.
53
+ 4. Do not automatically retry failed keys. Report safe API error codes and
54
+ counts; a later invocation can audit and retry what remains.
55
+ 5. Re-list every inspected purpose prefix and report the remaining candidate
56
+ count and bytes.
57
+
58
+ ## Don't
59
+
60
+ - Don't create a Worker, Cron Trigger, lifecycle rule, D1 table, or local script.
61
+ - Don't use prefix deletion or empty-bucket operations.
62
+ - Don't delete private-media buckets or objects outside declared purpose
63
+ prefixes.
64
+ - Don't treat missing pagination pages, metadata, or permissions as an empty
65
+ result.
66
+
67
+ ## Diagnostics
68
+
69
+ | Symptom | Action |
70
+ |---|---|
71
+ | Multiple matching accounts or buckets | Stop and ask the user to select the exact target. |
72
+ | A page is truncated without a cursor | Stop; do not delete from a partial audit. |
73
+ | A candidate has an unexpected key or metadata shape | Skip it and include it only in the aggregate skipped count. |
74
+ | A delete request partially fails | Report safe error codes and leave the remaining objects for a later audit. |
75
+
76
+ ## When You're Done
77
+
78
+ Return whether the run was audit-only or applied, the aggregate before/after
79
+ counts and bytes, and any safely redacted failures.