Skip to content

feat(profile): add project profile sync commands#72

Merged
Ryan Zhu (underthestars-zhy) merged 12 commits into
photon-hq:mainfrom
gtxy27:feat/per-line-imessage-profile
Jul 14, 2026
Merged

feat(profile): add project profile sync commands#72
Ryan Zhu (underthestars-zhy) merged 12 commits into
photon-hq:mainfrom
gtxy27:feat/per-line-imessage-profile

Conversation

@gtxy27

@gtxy27 gtxy27 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add photon spectrum profile sync to align the saved Project Profile across active dedicated iMessage Lines through the Dashboard BFF
  • add per-Line name editing with photon spectrum lines profile update <line-id>
  • add per-Line avatar upload with photon spectrum lines avatar upload <line-id> <file> using the signed upload → PUT → commit flow
  • make avatar commit the final persistence step for the existing Project avatar command, removing the obsolete --avatar-url, --no-update-profile, and post-commit Profile PATCH paths
  • consume the updated typed routes from @photon-ai/dashboard-api@1.7.0

Semantics

  • profile sync performs one POST and returns { projectId, syncedLineCount }; it does not poll or expose a status command
  • HTTP success means the Line Profiles are aligned in the database; worker/application state is not part of the Dashboard or CLI contract
  • syncedLineCount is the number of active dedicated iMessage Lines aligned after the request, including Lines that already matched
  • per-Line name updates preserve omitted fields
  • avatar upload is finalized by commit; no follow-up avatarUrl Profile PATCH is sent

Commands

photon spectrum profile sync
photon spectrum lines profile update <line-id> [--first-name <name>] [--last-name <name>]
photon spectrum lines avatar upload <line-id> <file>

All commands support the existing Project/API host/token resolution. Sync and name update also support --json where applicable.

Dependency / rollout

  • depends on dashboard #240 and spectrum-cloud #108
  • @photon-ai/dashboard-api@1.7.0 is not published yet, so fresh frozen installs and CLI CI remain blocked until that package is published; after publication, rerun this PR's checks

Validation

  • TypeScript typecheck passes locally
  • all 41 CLI tests pass locally
  • Bun produces the CLI bundle successfully
  • contract tests verify the sync command performs one POST without polling and cover per-Line name and avatar operations

Scope

  • no sync-status command, GET polling, syncId, worker status, revision state, or Line-level worker errors
  • no server endpoints are added by this PR

Note

Medium Risk
Behavior changes on existing avatar/profile commands and a large dashboard-api bump; risk is moderated by contract tests and client-side SVG blocking before API calls.

Overview
Extends Spectrum CLI for project–line profile management against a newer @photon-ai/dashboard-api (1.2.0 → 1.6.12).

photon spectrum profile sync triggers a single POST to align the saved project profile across dedicated iMessage lines and reports targetedLineCount (no polling). photon spectrum lines profile update and photon spectrum lines avatar upload add per-line name edits and the same presigned upload → PUT → commit flow used for project avatars.

Avatar handling is centralized in ~/lib/presigned-upload.ts (local file read, MIME mapping, SVG rejection by extension or content). Project spectrum avatar upload uses that helper and stops auto-patching the profile after commit (--no-update-profile, recovery hints, and --avatar-url on profile update are removed). spectrum lines list reads data.lines and shows displayPhoneNumber when phoneNumber is absent.

Contract tests and mock routes cover sync, line profile PATCH, line avatar faults, and SVG rejection.

Reviewed by Cursor Bugbot for commit 63f3885. Bugbot is set up for automated code reviews on this repo. Configure here.

Copilot AI review requested due to automatic review settings July 12, 2026 06:56

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Spectrum profile synchronization, line profile and avatar commands, shared presigned-upload handling, updated line response compatibility, and contract coverage for the new flows.

Changes

Spectrum management commands

Layer / File(s) Summary
Profile synchronization command
src/commands/spectrum/profile.ts
Registers profile sync, performs the authenticated POST request, handles API responses, and renders JSON or human-readable results.
Line profile and avatar commands
src/commands/spectrum/line-profile.ts, src/commands/spectrum/lines.ts
Adds line profile updates and avatar upload/commit flows, and wires them into Spectrum line commands.
Shared presigned upload handling
src/lib/presigned-upload.ts, src/commands/spectrum/avatar.ts
Centralizes local file metadata and presigned PUT handling, then updates avatar upload to use the shared helpers.
Mock API and contract coverage
tests/helpers/mock-server.ts, tests/contract/*spectrum*
Adds mock profile-sync behavior and contract tests for synchronization, profile updates, and avatar uploads.
Spectrum lines compatibility
package.json, src/commands/spectrum/lines.ts
Updates the Dashboard API dependency and handles nested line data and alternate phone-number fields.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as Spectrum CLI
  participant DashboardAPI as Dashboard API
  participant Storage as Presigned storage
  CLI->>DashboardAPI: Request avatar upload URL
  DashboardAPI-->>CLI: Return upload URL
  CLI->>Storage: PUT avatar payload
  Storage-->>CLI: Return upload status
  CLI->>DashboardAPI: Commit avatar upload
  DashboardAPI-->>CLI: Return avatar result
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description does not follow the repo template; it omits the Upstream version, Routes, Snapshot changes, Runtime deps, and Checklist sections. Rewrite it using the template headings and add the missing route list, snapshot notes, dependency notes, and checklist items.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately covers the new profile sync command, though the PR also includes related line profile and avatar command work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 52: Update the `@photon-ai/dashboard-api` dependency in package.json from
unavailable version 1.7.0 to a currently published registry release, ensuring
installs and typechecks resolve successfully.

In `@src/commands/spectrum/profile.ts`:
- Around line 279-295: Update finishProfileSync to accept context identifying
whether it was called by sync or sync-status, and use that context to avoid
suggesting sync-status when already running that command. Adjust every
finishProfileSync call site accordingly while preserving the existing failure
messages and aggregate output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3d848948-d0a1-483a-b71d-202da48927c5

📥 Commits

Reviewing files that changed from the base of the PR and between c9fd269 and 180993e.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock, !bun.lock
📒 Files selected for processing (5)
  • package.json
  • src/commands/spectrum/lines.ts
  • src/commands/spectrum/profile.ts
  • tests/contract/spectrum-profile-sync.contract.test.ts
  • tests/helpers/mock-server.ts

Comment thread package.json Outdated
Comment thread src/commands/spectrum/profile.ts Outdated
Comment on lines +279 to +295
function finishProfileSync(
result: ProfileSyncAggregate,
json: boolean
): void {
printProfileSyncAggregate(result, json);

if (result.status === "partial_failed") {
die("Spectrum profile sync completed with failed lines.", {
hint: "Run `photon spectrum profile sync-status` to inspect the current aggregate.",
});
}
if (result.status === "failed") {
die("Spectrum profile sync failed.", {
hint: "Run `photon spectrum profile sync-status` to inspect the current aggregate.",
});
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

finishProfileSync hint is misleading when called from sync-status.

The hint "Run photon spectrum profile sync-status to inspect the current aggregate" is shown for partial_failed/failed in both sync and sync-status commands. When the user just ran sync-status, telling them to run it again is unhelpful and confusing.

💡 Proposed fix: add a context parameter to differentiate the calling command
 function finishProfileSync(
   result: ProfileSyncAggregate,
-  json: boolean
+  json: boolean,
+  command: "sync" | "sync-status" = "sync"
 ): void {
   printProfileSyncAggregate(result, json);

   if (result.status === "partial_failed") {
     die("Spectrum profile sync completed with failed lines.", {
-      hint: "Run `photon spectrum profile sync-status` to inspect the current aggregate.",
+      hint: command === "sync"
+        ? "Run `photon spectrum profile sync-status` to inspect the current aggregate."
+        : "Re-run `photon spectrum profile sync` to retry failed lines.",
     });
   }
   if (result.status === "failed") {
     die("Spectrum profile sync failed.", {
-      hint: "Run `photon spectrum profile sync-status` to inspect the current aggregate.",
+      hint: command === "sync"
+        ? "Run `photon spectrum profile sync-status` to inspect the current aggregate."
+        : "Re-run `photon spectrum profile sync` to retry.",
     });
   }
 }

Then update the call sites:

-      finishProfileSync(result, opts.json ?? false);
+      finishProfileSync(result, opts.json ?? false, "sync");
-      finishProfileSync(result, opts.json ?? false);
+      finishProfileSync(result, opts.json ?? false, "sync-status");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/spectrum/profile.ts` around lines 279 - 295, Update
finishProfileSync to accept context identifying whether it was called by sync or
sync-status, and use that context to avoid suggesting sync-status when already
running that command. Adjust every finishProfileSync call site accordingly while
preserving the existing failure messages and aggregate output.

Copilot AI review requested due to automatic review settings July 12, 2026 13:43

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 13, 2026 03:13

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 13, 2026 03:23

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 13, 2026 04:49

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/presigned-upload.ts`:
- Around line 1-31: When updating readLocalUploadFile to use Bun.file, preserve
the existing MIME behavior by using MIME_TYPES based on the file extension and
falling back to application/octet-stream for unknown extensions; do not use
bunFile.type directly when it reports Bun’s default text/plain;charset=utf-8.
- Around line 33-48: Add a 60-second timeout to the fetch request in
putPresignedUpload by passing AbortSignal.timeout(60_000) as its signal option,
ensuring both upload command call sites inherit the timeout without changing
existing response handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c047914c-686b-4c2e-b252-dc636bc10ae4

📥 Commits

Reviewing files that changed from the base of the PR and between ae6e888 and b74f0b2.

📒 Files selected for processing (6)
  • src/commands/spectrum/avatar.ts
  • src/commands/spectrum/line-profile.ts
  • src/commands/spectrum/lines.ts
  • src/lib/presigned-upload.ts
  • tests/contract/spectrum-line-profile.contract.test.ts
  • tests/helpers/mock-server.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/helpers/mock-server.ts

Comment thread src/lib/presigned-upload.ts Outdated
Comment on lines +33 to +48
export async function putPresignedUpload(
file: string,
uploadUrl: string,
upload: LocalUploadFile
): Promise<void> {
console.log(c.dim(`Uploading ${file} (${(upload.size / 1024).toFixed(1)} KB)…`));

const response = await fetch(uploadUrl, {
method: "PUT",
body: upload.body,
headers: { "Content-Type": upload.contentType },
});
if (!response.ok) {
die(`Upload failed: ${response.status} ${response.statusText}`);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Add a timeout to the presigned PUT request.
fetch here has no signal/timeout, so a slow or unreachable storage endpoint can stall the CLI. This shared implementation is used by both upload commands, so adding signal: AbortSignal.timeout(60_000) here covers both call sites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/presigned-upload.ts` around lines 33 - 48, Add a 60-second timeout to
the fetch request in putPresignedUpload by passing AbortSignal.timeout(60_000)
as its signal option, ensuring both upload command call sites inherit the
timeout without changing existing response handling.

Copilot AI review requested due to automatic review settings July 13, 2026 05:09

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 14, 2026 01:03

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 14, 2026 02:29

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 14, 2026 08:47

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ac39b17. Configure here.

Comment thread src/commands/spectrum/profile.ts
Comment thread src/commands/spectrum/line-profile.ts Outdated
Comment thread src/commands/spectrum/profile.ts Outdated
Comment thread src/commands/spectrum/line-profile.ts Outdated
Copilot AI review requested due to automatic review settings July 14, 2026 10:03

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 14, 2026 10:10

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@underthestars-zhy
Ryan Zhu (underthestars-zhy) merged commit 1d1a4f7 into photon-hq:main Jul 14, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release Just as it is

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants