Skip to content

feat(deploy): share strip and Next steps trail under the Deployment Task Success Record (AIM-354) - #345

Merged
aimeritething merged 9 commits into
mainfrom
aimeritething/aim-354-deployment-task-success-record-share-strip-and-next-steps
Sep 8, 2026
Merged

feat(deploy): share strip and Next steps trail under the Deployment Task Success Record (AIM-354)#345
aimeritething merged 9 commits into
mainfrom
aimeritething/aim-354-deployment-task-success-record-share-strip-and-next-steps

Conversation

@aimeritething

Copy link
Copy Markdown
Member

What

When a deployment verifies, the Deployment Task Success Record now ends with two quiet blocks under the card:

  • Share stripShare <product>, a QR popover (Open on your phone + host, dark-on-light tile, viewfinder corners), and one-click posts to X, LinkedIn, Facebook and Reddit. Shown only when the record has a primary HTTP(S) entry; always shares that entry's URL exactly as snapshotted. Every link opens a new tab with rel="noopener noreferrer", so nothing of Brain is shared.
  • Next steps trail — the declared first-use steps move out of the card into a numbered trail (circle, hairline, label, monospace detail) under a Next steps heading. Shown only when the record declares steps, independently of the strip.

The section root becomes a wrapper owning card, strip and trail, so the scroll-into-view target and the deployment-task-success slot cover the whole conclusion. The Timeline pane still renders one section (ADR-0078).

Share copy

The X post speaks in the user's voice and names Sealos. The voice is chosen from facts the record snapshotted, never from the live catalog:

  1. productId is eaglercraft-server → the EaglerCraft voice (the one deliberate product branch; supersedes feat(deploy): celebrate verified deployment success in the Timeline #336's "no product branch").
  2. Otherwise the first snapshotted category: game or ai.
  3. Otherwise the generic launch post.

A record without a product name drops the name rather than inventing one. No post promises a deployment time. Reddit's link-post title is <name> is live — just shipped with Sealos. LinkedIn and Facebook share the address alone.

Record contract

To make that possible, a template-sourced Deployment Task now carries templateCategories from the catalog item the user chose, and the runner writes the template name into the record's productId (declared, never filled before) and the categories into a new optional productCategories. The sanitizer trims, de-duplicates and caps the list; the record's signature includes it. Contract version unchanged; older records simply lack the fields; other sources declare neither.

Also

  • The share module is feature-local (deployment-task-success-share.tsx); it moves to @workspace/ui only if a second consumer appears.
  • Share controls compose AppIconButton through its render prop, matching the card's copy controls.
  • The success section gets a little bottom padding so the conclusion sits off the pane's edge.
  • CONTEXT.md's Success Record entry gains the sharing, Next steps, and snapshot sentences.
  • qrcode.react added; the deployment-success-share prototype route tree removed.

Tests

  • Pane markup tests: ordering (card → strip → trail), WS-only and no-entry records render no strip, four channel links with encoded hrefs / target / rel / labels plus the QR trigger, nameless record, EaglerCraft and AI voices, trail with and without a strip, no heading without steps.
  • Pure-function tests for each voice (name present/absent), voice order, %0A line breaks, #/&/space encoding, Reddit title, QR panel content.
  • Category pipeline at each layer: task source schema keeps and caps templateCategories; pipeline copies them into the source; source→product helper and readiness claim carry id and categories; sanitizer trims/de-dupes/caps productCategories; signature includes them.
  • bun typecheck, bun check clean.

Out of scope

Per-template share copy declared by the template itself (beyond the EaglerCraft voice); any producer of first-use steps (AIM-353); Web Share API, WeChat/Weibo, copy-link in the strip, localisation, analytics.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M

aimeritething and others added 5 commits September 8, 2026 14:33
… (AIM-354)

When a deployment verifies, the moment a product goes live is the moment
people want to show it to someone. The Deployment Task Success Record now
carries a quiet share strip under the card — `Share <product>`, a QR
popover for a phone (`Open on your phone` + host, dark-on-light in both
themes, viewfinder corners), and one-click posts to X, LinkedIn, Facebook
and Reddit. It appears only when the record has a primary HTTP(S) entry and
always shares that entry's URL exactly as the record snapshotted it; every
link opens a new tab with no referrer, so nothing of Brain is shared.

The declared first-use steps move out of the card, where they competed with
the verified facts, into a `Next steps` trail below the strip: a numbered
circle per step, a hairline between them, the label and its optional
monospace detail. It renders only when the record declared at least one
step, independently of the strip.

The section root becomes a wrapper owning card, strip and trail, so the
scroll-into-view target and the `deployment-task-success` slot cover the
whole conclusion; the Timeline pane still renders one section (ADR-0078).
The share module is feature-local and its copy/href builders are pure. No
contract, sanitizer, API or runner change.

Ships the CONTEXT.md sentences for sharing and `Next steps`, adds the
qrcode.react dependency, and removes the deployment-success-share prototype
route now that its Merge variant has been promoted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
Review follow-up for AIM-354. The QR trigger and the four channel links
now compose AppIconButton through its render prop, as the copy controls
and the Open link already do, instead of reaching for the raw variants.
The numbered circle uses the text-[11px] form the card already had, the
viewfinder corners are one mapped list, the encoder alias is gone, and
`Next steps` is a real heading. The href test now covers a space inside
the address itself, not only inside the product name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
The share strip's quiet icon buttons ended flush on the Timeline pane's
own padding, so the result read as cramped at the bottom. The section
wrapper now carries a little bottom padding of its own, so whichever
block ends the conclusion — the card, the strip, or the Next steps
trail — sits off the pane border.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
…ccess record

The Deployment Task Success Record could name the product but not say
what kind of product it was: a template's catalog categories stopped at
the catalog and the deploy form, never reaching the task or the record.
The share copy (AIM-354) wants to speak differently to a game server and
to an AI app, and reading the live catalog at render time would break the
rule that the record is a snapshot.

A template-sourced Deployment Task now carries `templateCategories` from
the catalog item the user chose, and the runner writes the template name
into the record's `productId` (declared in the contract, never filled
until now) and the categories into a new optional `productCategories`.
The sanitizer trims, de-duplicates and caps the list; the record's
signature includes it. Other sources declare neither, and records
written before this change simply lack the fields.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
The X post was a bare "Just launched <name> 🚀 <url>". It now reads like
the user announcing their own launch and names Sealos: the generic post,
plus a game-server voice, an AI-app voice, and one hard-coded voice for
the EaglerCraft server. The voice is chosen from the facts the success
record snapshotted — the product id first, then the first category —
never from the live catalog, so a record keeps the post it was written
with. A record without a product name drops the name rather than
inventing one, and no post promises a deployment time.

Reddit's link-post title becomes "<name> is live — just shipped with
Sealos" ("My app is live …" without a name). LinkedIn and Facebook
still share the address alone. The handle and hashtags are module
constants; the copy and href builders stay pure and tested.

This reverses two decisions recorded on AIM-354 (the 🚀 copy, and no
platform attribution) and #336's "no product branch"; the issue is
revised to match.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
Four lines run together read as a block, with the hashtags jammed under
a long wrapped link. The post now separates its announcement, its link
and its hashtags with blank lines, the way posts on X usually scan, and
the generic voice moves @Sealos_io up to the first line so every voice
opens the same way and the link stands alone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwJfKahqCYxMzJXGbF522M
@norberia

norberia commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

@cursoragent review this

@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown

Request changes on PR #345. The share strip, Next steps trail, snapshot rule, and tests are solid. The game/AI voices will not fire on the deployments people actually share.

templateCategories is only passed from the in-project Template pane and GitHub-template recommendation. New-project template create and chat createDeployTask already have the catalog item and drop the field, so the record has productId (EaglerCraft still works) but no productCategories (everything else falls through to generic). CONTEXT.md currently claims every template record snapshots categories. Wire choice.category / input.template.category in use-project-creator.ts, and resolve categories from the catalog at chat create rather than trusting the model to copy them.

Contract v2, sanitizer, signature, HTTP(S)-only sharing with noopener noreferrer, and ADR-0078 (one Timeline section) all hold. Smaller follow-ups: match house-style AppIconButton link composition, name the QR dialog, prefer text-2xs, and don’t treat the template slug as a display name in share copy.

Full walkthrough: AIM-354 PR #345 review. The same notes are on the PR.

Open in Web Open in Cursor 

@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown

Review

Request changes. The share strip, Next steps trail, snapshot rule, and tests are in good shape. The category pipeline that the new game/AI voices depend on is not wired on the two first-launch create paths, so those voices will not fire for the deployments people actually share.

Blocking: templateCategories never leaves the in-project panes

The design is right: share copy reads the record, never the live catalog. A template task only gets productCategories if the create request already carried templateCategories. Two in-project panes do that. The first-success paths do not, even though they already have the catalog item in hand:

Create path Has catalog item Passes templateCategories
In-project Template pane choice.category yes
In-project GitHub template recommendation template.category yes
New project · template (use-project-creator.ts onTemplateConfirm) choice.category no
New project · GitHub template recommendation (handleGithubTemplateDeploy) input.template.category no
Chat createDeployTask catalog tool already returns categories stripped by chat-deploy-task-input.ts

productId still lands (source.templateName was always there), so the EaglerCraft voice still works. Every other template falls through to generic. Clones inherit the predecessor source, so the first successful deploy — the moment this strip exists for — is the one that matters.

CONTEXT.md currently says a template record snapshots the declared categories. That is only true for the two in-project panes.

Fix: pass templateCategories: choice.category / input.template.category in the two use-project-creator.ts calls, matching template-deployment-pane.tsx and github-deployment-pane.tsx. For chat, do not trust the model to copy the field: resolve categories from the catalog by templateName at create time (or add the field to the tool schema and fill it in the tool). Add a test on each create site.

What holds

  • Contract stays at v2: productId was already optional; productCategories is additive. Older records just lack the fields.
  • Sanitizer trims, de-dupes, caps at 16, omits empty lists; signature includes categories.
  • Share/Open use the snapshotted primary HTTP(S) entry, rel="noopener noreferrer". This is not ADR-0015 preview sharing. WS-only records render no strip.
  • Next steps is independent of the strip; no heading when the record declared no steps. One Timeline section (ADR-0078).
  • Voice order (EaglerCraft id → first category game/ai → generic), nameless records, no promised deploy time, X three-beat body, Reddit title, LinkedIn/Facebook URL-only, encoding of # / & / space / %0A.

Non-blocking

  1. Channel AppIconButton composition (deployment-task-success-share.tsx): icon lives inside the <a>, children are {null}. House style (e.g. billing-tab-shell.tsx) is an empty render host with the icon as children. Pane tests only capture the opening <a> tag, so an empty merge would still pass. Match the house pattern and assert the icon is inside the link.
  2. QR popover a11y: Base UI Popover is role="dialog" with no PopoverTitle / aria-label. Promote “Open on your phone” to PopoverTitle.
  3. New text-[11px]: globals.css already registers text-2xs. Use that on the new surfaces.
  4. productName is the template slug. Share copy will say eaglercraft-server, while the tests use display titles. Snapshot choice.title if the post should name the product. The dedicated EaglerCraft voice is unaffected.
  5. successText: comment says “single line”; it only trim()s. Interior newlines in productName would break the X beats. Unlikely from the catalog.
  6. Sanitizer cap test asserts length < 40, not exactly 16.

I did not run the UI in a browser. Review is against the source, ADRs, CONTEXT.md, and the AIM-354 tests.

Structured walkthrough: AIM-354 PR #345 review

aimeritething and others added 3 commits September 8, 2026 17:41
The share voices read `productCategories` off the success record, but
only the two in-project panes sent `templateCategories` with the create
request. New-project template deploys and the chat `createDeployTask`
tool — the first-success paths the share strip exists for — dropped the
field, so every template but EaglerCraft fell through to the generic post.

Both new-project template paths now build their request through
`newProjectTemplateRequest`, which carries the chosen catalog item's
categories the way the panes do. The chat tool resolves categories from
the catalog by template name at create time rather than trusting the
model to copy them; an unknown name or an unreachable catalog leaves the
source as declared and never blocks the deploy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L75qX7NHHC165cykRCyMGG
Channel links compose `AppIconButton` as the rest of the app does: an
empty anchor as the render host, the icon as children. The pane test now
captures the whole `<a>` element and asserts the icon sits inside it.

The QR popover's "Open on your phone" line becomes its `PopoverTitle`, so
the dialog Base UI announces carries a name. The new 11px surfaces use
the registered `text-2xs` token instead of an arbitrary size.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L75qX7NHHC165cykRCyMGG
`successText` promised a single line but only trimmed, so an interior
newline in a product name would have broken the X post's beats. It now
folds whitespace runs to one space. The sanitizer test asserts the
category cap exactly (16) instead of merely "fewer than 40".

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L75qX7NHHC165cykRCyMGG
@aimeritething
aimeritething merged commit bc579af into main Sep 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants