Add an attachment-prune subcommand for orphaned attachments
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 2
- Forks
- 0
- Avg merge
- 2h 8m
- Merged PRs (30d)
- 49
Description
markfluence never deletes, so an attachment it uploaded stays on the page after the page stops referencing it. There is no way to clean those up except by hand in the Confluence UI. Add attachment-prune PAGE to remove them.
What makes an attachment prunable
Two conditions, both required:
- Unreferenced. The page body does not name it.
cmd/export/export.goalready computes this asreferencedNames(storage), matchingri:filenameanywhere in the raw storage rather than only insideac:image— an attachment link target or a reference inside a passthrough macro counts as referenced. - Managed by markfluence.
client.AttachmentMeta.Managedis true only for attachments carrying themarkfluence:comment prefix. This half is not optional: an attachment someone added in the Confluence UI is unreferenced by definition if it is not embedded, so pruning on "unreferenced" alone would delete a PDF a human attached for reference. That is a S5 violation.
Report the checksum, do not branch on it
Every managed attachment records a SHA-256 in its comment, which makes it possible to say whether deleting an orphan loses content: if its checksum matches an attachment the page still references, the bytes survive under another name. If it matches nothing, this is the only copy on the page.
That distinction should be reported, not encoded as different behaviour. Branching on it guesses; showing it lets the operator decide.
$ markfluence attachment-prune docs/guides/index.md --dry-run
orphan assets%2Fbrand.png content also attached as ..%2Fassets%2Fbrand.png
orphan old-diagram.png only copy on this page
keep handbook.pdf not managed by markfluence
Worth noting that the second case is usually still safe: a managed attachment came from a local file, so republishing reproduces it. The exception is when that file has since been deleted from the repo — which is exactly when the checksum will not match anything.
Guarantees
This command exists in the shape it does because of docs/guarantees.md:
- S4 — removal is a command's stated purpose or it does not happen. This is why pruning must not be a step inside
update. Publishing that quietly deleted things would be removal as a side effect. - S5 — markfluence removes only what markfluence created. The
Managedgate. - S6 — a command that removes says what it will remove first and honours
--dry-run.
S4-S6 are currently Vacuous: nothing in markfluence removes anything. This would be the first command to exercise them, so their status changes when it lands.
Shape
attachment-prune PAGE, matching its three siblings — singular noun, noun-first, so attachment-<TAB> still completes as a group. PAGE goes through internal/pageref like the others, so it accepts a page id, a Confluence URL, or a .md file with a page_id — pointing at the markdown file whose images just changed is the obvious way to invoke this.
Single page to start, like attachment-list. Batch pruning across a space has a much larger blast radius and can follow if it is wanted.
Prerequisites
- A delete method in the client. There is none:
http.MethodDeleteappears only insend's idempotent-methods list. referencedNameshas to move somewhere shared. It is local tocmd/exporttoday, and export and prune must not each have their own scanner — same rule as the traversal clamp ininternal/attachfile.- A schema entry: the
commandenum plus anif/thenbranch constrainingresults.itemsandsummary, orinternal/schematestfails.
Relationship to the file-organization work
The root-relative naming model in _plans/025_file-organization.md removes the most common source of orphans: today, moving a markdown file renames every attachment it references, because the name encodes the path relative to the referencing file. Under that model moving a page changes nothing, so nothing is stranded.
This command handles what is left over: moving or renaming an asset (whose identity does follow its location, by design), removing an image from a page, and orphans that already exist from before the model lands. It is a cleanup tool for the residual rather than a fix for the naming problem.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with cmd/export/export.go for referencedNames, then inspect internal/pageref, internal/attachfile, the client attachment metadata, and internal/schematest. Read docs/guarantees.md and the attachment-list siblings before defining the command. Done means a single-page attachment-prune command supports dry-run reporting, managed-only deletion, page references, and the required schema entry.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100