Skip to content

Rebuild onboarding around the family photo - #13

Open
maceip wants to merge 2 commits into
mainfrom
exp/onboarding-family-photo
Open

Rebuild onboarding around the family photo#13
maceip wants to merge 2 commits into
mainfrom
exp/onboarding-family-photo

Conversation

@maceip

@maceip maceip commented Sep 6, 2026

Copy link
Copy Markdown
Owner

What

Onboarding is rebuilt around one task: get your agent into the family photo, a group picture of everyone who has joined the project, agents and people alike. A portrait is an image an agent made of itself, uploaded as a blob and posted as a comment in the family-photo session; join order is your spot, front row first, rows widening and shrinking toward the back, scaling to a thousand (sharding to family-photo-2, … past the 256-comment cap).

  • ui/src/family/FamilyPhoto.tsx: deterministic seat plan, placeholder hillside until the real scene image arrives (scene prop), your portraits ringed, the next open spots drawn
  • ui/src/family/Onboarding.tsx: three steps that complete themselves — your spot; connect your agent with a copyable axp park command that ticks when the agent appears in the registry; the self-portrait task with the prompt, which a maintainer can send (automation on agent arrival is the intended path)
  • Gateway: GET /api/family lists portraits in join order; GET /api/portrait serves image blobs inline with nosniff, a sandboxed CSP, a 1.5 MB cap and immutable caching — the only blobs served as images; page CSP allows blob: images
  • Demo fixture posts seven procedural portraits; a browser test covers the photo, ordering, endpoint headers, unauthenticated refusal and the dialog
  • No protocol change; the note records the path to a dedicated portrait command

Second commit points the task prompt at the portrait generator in maceip/axp-avatar (branch axp/generative-inputs), which draws everyone in one style.

Look at

Verification

npm run check and npm run test:ui (8 tests) pass.

Landing

No conflicts with siblings.

maceip added 2 commits September 6, 2026 10:24
Onboarding is now one task: get your agent into the project's family photo,
a group picture of everyone who has joined. A portrait is an image an agent
made of itself, uploaded as a blob and posted as a comment in the
`family-photo` session; join order is your spot, front row first, rows
widening and shrinking toward the back, scaling to a thousand.

- ui/src/family: FamilyPhoto (deterministic seat plan, placeholder hillside
  until the real scene image arrives, your portraits ringed, next spots
  drawn) and Onboarding (three steps that complete themselves: your spot,
  connect your agent with a copyable park command that ticks when the agent
  appears, the self-portrait task with the prompt a maintainer can send)
- Gateway: /api/family lists portraits from family-photo sessions in join
  order; /api/portrait serves image blobs inline with nosniff, a sandboxed
  CSP, a 1.5 MB cap and immutable caching; page CSP allows blob: images
- Demo fixture posts seven procedural portraits; browser test covers the
  photo, ordering, endpoint headers, unauthenticated refusal and the dialog
- No protocol change; docs/design/onboarding.md records the design and the
  path to a dedicated portrait command
The self-portrait prompt now names the project's own composer
(maceip/axp-avatar, tools/compose.py --seed <principal> --transparent) as
the way to draw yourself in the family's style, alongside an agent's own
image tool. docs/design/onboarding.md describes the generator and the
provenance caveat on its source sheets.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 6, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-06T22:16:13.928838Z 87047e3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 87047e3763

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

useEffect(() => {
api<FamilyPhotoData>("family").then(setFamily, () => setFamily(undefined));
}, [workspace.receivedAt]);
const session = family?.sessions[0] ?? "family-photo";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Provision the fallback family session before using it

On a normal or upgraded host where no family-photo session has been manually created, /api/family returns an empty sessions array and this fallback merely invents the ID; it does not create the session. Consequently, the generated axp park command fails when it snapshots the nonexistent exchange, while both “Open the photo session” and the maintainer task action target missing channels. Create the session for an authorized maintainer or block these actions with setup guidance instead of treating the fallback as an existing session.

Useful? React with 👍 / 👎.

executor.online &&
executor.expiresAt > Date.now(),
);
const command = `axp park ${session} --profile .axp/contributor.json --native -- YOUR_ACP_AGENT`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid parking every contributor in one leased session

Once the first contributor follows this command and leaves axp park running after the portrait turn, its renewable lease remains attached to this shared session; Sessions.claim() rejects every subsequent claim with “Session already has an executor” (src/sessions.ts:324-328). Since all users receive the same session ID and the onboarding flow never tells the first user to stop, later contributors cannot join the photo. Use participant-specific work sessions or explicitly release the shared lease after the portrait task.

Useful? React with 👍 / 👎.

Comment on lines +38 to +39
const width = (count - 1) * pitch;
const start = SCENE.width / 2 - width / 2 + (row % 2 ? pitch / 2 : 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Cap each row's width to the scene bounds

The row count grows faster than the pitch shrinks, so row 2 already has a computed width of about 1,692 px in a 1,600 px scene and starts at a negative x-coordinate. The 30th portrait is therefore centered outside the frame, whose overflow: hidden clips it, and later rows lose progressively more people despite the advertised 1,000-person capacity. Derive the pitch or count from the available scene width so every generated seat remains visible.

Useful? React with 👍 / 👎.

Comment thread src/workspace.ts
Comment on lines +262 to +267
for (const item of items) {
const session = id.parse(item.resource.slice("ahp-session:/".length));
const state = await this.snapshot<ExchangeState>(
client,
channels(session).exchange,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid serially snapshotting the full session catalog

For repositories with many contributions, every family-photo refresh now performs one awaited subscription RPC for every readable session before it can determine which sessions have the family task. With a remote host, roughly 150 sessions at 100 ms latency already exceed the UI's 15-second API timeout, and above the workspace's 128-snapshot cache bound the sequential traversal evicts entries that the next refresh needs, causing the scan to repeat. Filter using catalog metadata or fetch with bounded concurrency and a durable task index/cache.

Useful? React with 👍 / 👎.

<summary>The prompt your agent receives</summary>
<pre>{FAMILY_TASK_PROMPT}</pre>
</details>
{maintainer && !inPhoto && (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the maintainer task action available for later agents

After a maintainer posts their own portrait, inPhoto remains true and permanently hides this manual task button, even when a later contributor has parked a new agent that still needs the portrait prompt. The planned arrival automation is not implemented in this change, so the maintainer-specific fallback is effectively usable only before that maintainer joins the photo; base its visibility on the current agent/turn state rather than the maintainer's portrait.

Useful? React with 👍 / 👎.

Comment on lines +223 to +228
useEffect(() => {
let cancelled = false;
let promise = urls.get(key);
if (!promise) {
promise = authorizedFetch(
`portrait?session=${encodeURIComponent(portrait.session)}&digest=${portrait.digest}`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Defer portrait fetches until each person is visible

Despite the comment claiming lazy loading, every mounted Person invokes this effect immediately, so opening the People page starts authenticated downloads for every portrait at once. At the advertised capacity this can enqueue 1,000 requests and retain up to roughly 1.5 GB of object-URL data even though many portraits are off-screen; the 20-second per-fetch timeouts also begin while requests wait for browser connection slots. Use an intersection observer or equivalent visibility signal before calling authorizedFetch.

Useful? React with 👍 / 👎.

Comment on lines +26 to +27
2. Upload it to this session with _axp/blobPut (mediaType image/png or image/svg+xml).
3. Post one comment with _axp/comment whose body is the image reference followed by a one-line caption, for example:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Expose the portrait protocol operations to the parked agent

The parked ACP agent cannot execute either operation named in this prompt: AcpDriver starts the agent with no MCP servers and no client filesystem or terminal capabilities (src/acp.ts:243-275), and the satellite never exposes _axp/blobPut or _axp/comment as agent-callable tools. Its internal blob callback only uploads protocol artifacts, while there is no comment callback at all. Thus an agent may create an image with its own tools but cannot upload it or claim a photo spot; add an agent-facing portrait tool/command or have the satellite perform these operations from an explicit agent result.

Useful? React with 👍 / 👎.

executor.online &&
executor.expiresAt > Date.now(),
);
const command = `axp park ${session} --profile .axp/contributor.json --native -- YOUR_ACP_AGENT`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the active workspace identity in the park command

For the default maintainer workspace, or any contributor using a custom profile path, this hard-coded profile launches the agent under a different token from workspace.principal. The completion checks still look for an executor and portrait authored by the current workspace principal, so the copied command can succeed while steps 2 and 3 never complete for that user; in the default initialized setup it attributes both to the separate contributor identity instead of the maintainer. Generate guidance for the same profile used to open the workspace, or stop presenting the status as belonging to the current principal.

Useful? React with 👍 / 👎.

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.

1 participant