Skip to content

feat: carry item origin, share form, and recipe in the HolaHub ShareDraft - #444

Merged
jeffreyliimerch merged 8 commits into
mainfrom
feat/holahub-share-draft-provenance
Jul 28, 2026
Merged

feat: carry item origin, share form, and recipe in the HolaHub ShareDraft#444
jeffreyliimerch merged 8 commits into
mainfrom
feat/holahub-share-draft-provenance

Conversation

@jotyy

@jotyy jotyy commented Jul 28, 2026

Copy link
Copy Markdown
Member

The ShareDraft is the only channel from a real desktop run to a HolaHub post, so anything a post wants to claim has to travel on it. This is the holaOS half of the post provenance contract; the backend and web halves land separately.

What changes

  • ShareDraftItem gains a required origin. derived is what was detected from what the sharer actually invoked — it renders in the post's "Made with" row and is not theirs to remove, only to demote. attached is a plain recommendation they can drop. Also adds the integration type, which the catalog already has but the draft could not express.
  • ShareDraft gains form — which share mode produced it. It settles exactly one question: which is the hero when a draft carries both a transcript and media. It is not a post taxonomy; absent means "not a desktop share".
  • ShareDraft gains recipe ({ prompt, model }) — what Reproduce seeds into a new session.
  • Attribution items resolved from an output's module_id are marked derived, since that is exactly a detection.

Also drops the "Everything here is user-editable in the composer" note on ShareDraft, which had been contradicting the "auto-attributed" note directly above it — that contradiction is what this contract resolves.

Notes

Types and capture only. Nothing renders the two zones yet, and richer detection (session skillIds → skill, tool_call events) is a follow-up — this PR just stops the draft from throwing away what it already knows.

@holaboss/app-host and the desktop typecheck clean on this branch.

🤖 Generated with Claude Code

@jotyy
jotyy requested a review from jeffreyliimerch as a code owner July 28, 2026 02:42
jotyy and others added 3 commits July 28, 2026 13:34
…raft

The draft is the only channel from a real run to a post, so anything the post
wants to claim has to travel on it.

- ShareDraftItem gains a required origin: "derived" is what was detected from
  what the sharer actually invoked and is not theirs to remove; "attached" is a
  recommendation they can drop. Also adds the integration type, which the
  catalog already has.
- ShareDraft gains form (which share mode produced it — the tie-breaker for
  which is the hero when a draft carries both a transcript and media) and
  recipe (the prompt + model that made the Output, for Reproduce).
- Attribution items resolved from an output's module_id are marked derived,
  since that is exactly a detection.

Drops the "Everything here is user-editable in the composer" note, which had
been contradicting the "auto-attributed" one directly above it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The draft could already carry a form and a recipe; nothing filled them, so the
web side was still inferring the shape from which fields happened to be set.

- ShareableOutput keeps input_id, and resolveRecipePrompt walks it back to the
  user turn that produced the artifact, falling back to the conversation's
  opening ask. Quoted-skill command lines are stripped: the skills travel as
  items, so leaving the raw /skill lines in would seed a prompt that only runs
  for someone who already has them.
- An outputs share now sends form "output" plus that prompt as its recipe; a
  conversation share sends form "conversation". The sharer already made this
  choice in the mode switch — it just wasn't travelling.

recipe.model stays empty for now. It is the session model that Reproduce hands
to chat.start, and capturing a structured id (rather than the display string the
pane renders) is its own change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`model` is what Reproduce seeds a new session with; `outputModel` is what
actually generated the artifact and exists to be shown to the reader. A share
can have both — an image made by a skill inside a chat session — so one field
always dropped one of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jotyy
jotyy force-pushed the feat/holahub-share-draft-provenance branch from 5587174 to e7cfaf1 Compare July 28, 2026 05:58
jotyy and others added 5 commits July 28, 2026 14:29
Attribution was one line of guesswork: every share credited the apps behind its
outputs and nothing else, so the skills someone picked out of the composer — the
thing they most obviously "made it with" — never travelled at all.

- gatherQuotedToolItems reads the skills and integrations quoted in the turns
  being shared. A quoted skill is a detection, so it ships as derived and the
  composer will not let the author drop it. An integration is a prerequisite
  rather than an actor, so it rides along as a recommendation.
- Detection is scoped to what actually travels: the turns kept in a conversation
  share, and for an outputs share the turns that produced the picked artifacts.
  Dropping a turn drops its credit, which is the point — you cannot keep the
  artifact and lose the tool.
- The Recipe now carries the resolvable model id rather than the display label,
  which was never something Reproduce could select again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reproduce promises "make your own equivalent", and the model is half of what
made the original — but chat.start had no way to say which one, so a shared
Output always reproduced on whatever the viewer happened to have selected.

ChatStartInput gains `model`, carried to ChatPane through the same request-atom
route the composer prefill already uses and applied through the same path as the
composer's own picker. A model this install does not have is ignored rather than
raised: the viewer still gets the session, the tools, and the prompt, and only
loses the exact match — blocking the hand-off over it would be worse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reproduce promises "one like this", and until now the viewer never saw "this" —
only a prompt describing it. ChatStartInput gains `attachments`, base64 over the
same IPC bridge a ShareDraft's images already use, decoded into the ordinary
pending-attachment path.

Going through that path rather than a bespoke one is the point: the composer's
own image-support check applies, and removing the reference is just removing an
attachment — which is what makes it optional without any extra affordance.

A malformed attachment is dropped rather than raised. Losing the reference is a
smaller failure than losing the hand-off it was riding along with.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A reader of a generated sample should be able to see what made it, and the
runtime already stamps that on the output — `model` on a written report,
`model_id` on a generated image. The share just never read it, so every desktop
post arrived with an empty outputModel while growth-pipeline posts had one.

Display-only, and deliberately separate from the session model: this is what
produced the artifact, not something Reproduce can open a conversation on.

Empty when the producing tool records no model. Image outputs are the case worth
watching — image_generate returns model_id to its caller, and whether that
reaches the output record is a runtime question this change does not answer; it
reads the convention and starts working the moment the stamp is there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Falling back silently let someone believe they had reproduced something on the
model it was made with. The hand-off still proceeds — tools, prompt, session —
but now it names the model the original used and says the default is standing in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jeffreyliimerch
jeffreyliimerch merged commit 359c7bb into main Jul 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants