Skip to content

feat(cli): feedback says whether a person or an agent wrote it, with the email an app attaches - #4984

Merged
miguel-heygen merged 3 commits into
mainfrom
feat/cli-feedback-source-email
Oct 4, 2026
Merged

miguel-heygen merged 3 commits into
mainfrom
feat/cli-feedback-source-email

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

What

hyperframes feedback can now say who wrote a report and, when an app launched the CLI for a signed-in person, carry that person's email.

  • --source person or --source agent marks the report. Any other value is refused before anything is sent.
  • The email comes only from the HYPERFRAMES_FEEDBACK_EMAIL environment variable, which the launching app sets. It is never a flag, and the CLI's project .env loading now skips that key, so an agent cannot attach one through a command line or a file it writes. It is not verified: a way to reply, not proof of who sent the report.
  • The feedback request gains two optional fields, source and email, the email capped at 254 characters. Without them the request body is exactly what it was before.

Why

Apps that embed the CLI send two kinds of feedback through hyperframes feedback: reports a person reviews and sends from a feedback button, and reports their agent files on its own. The team needs to tell the two apart when reading them, and to be able to reply to a signed-in person. Today both look the same and carry no way to reach the person.

Related work

An embedding app can pass --source person from its feedback button and --source agent for its agent, and set the email only while the person is signed in, once this ships in a CLI release. The feedback service reads the two new fields separately; until it does, they are ignored.

How

  • src/utils/feedbackSource.ts owns both values: parseFeedbackSource (absent, valid, or refused) and feedbackEmail (the one environment variable, trimmed, blank as absent).
  • The .env loader moved from src/cli.ts into src/utils/dotEnv.ts unchanged except that it skips the feedback email key; cli.ts still imports it lazily.
  • feedback checks --source right after --rating, so a bad value fails the same way whether or not telemetry is on.
  • submitFeedback adds the two fields to the existing POST; JSON.stringify drops them when undefined.
  • The email goes only into that POST. It is not added to the telemetry event, the printed output, the environment summary or a --file-issue draft.
  • feedback is no longer described as anonymous: it is, unless the launching app attaches an email.
  • Docs: the feedback section of the CLI reference and "About feedback data" in the feedback guide say what the new flag and variable do. --source applies to rating reports; a --search-miss report carries neither.

Test plan

  • Unit tests added/updated:
    • feedbackSource.test.ts: parsing and the variable.
    • feedback.test.ts: a person report carries the email, which stays out of the telemetry event and the environment summary; an agent report without the variable carries none; an unknown source is refused with nothing sent.
    • dotEnv.test.ts: the loader's existing behaviour, and that it never takes the email from a project file.
    • submitFeedback.test.ts: both fields reach the body and the email is capped.
  • 22/22 across the four files, three runs in a row. Each guard has a mutation that fails a test: removing the email from the submit call, accepting any source, dropping the .env skip, and adding the email to telemetry.
  • The existing exact-body test still passes unchanged, so callers that pass neither field send the same request as before.
  • Documentation updated.
  • Comments follow CONTRIBUTING.md "Comments".
  • Not exercised: a live POST to the feedback endpoint (the tests stub fetch), the embedding app's side, which lands separately, and cli.commands.test.ts on my box, where the engine's built @hyperframes/core subpaths were missing; CI runs it.

@mintlify

mintlify Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
hyperframes 🟢 Ready View Preview Oct 4, 2026, 2:51 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approving. The email comes from one place and goes to one place, --source fails closed, and the current server does ignore both new fields.

Email (PII)

  • The only reader is feedbackEmail(), which reads HYPERFRAMES_FEEDBACK_EMAIL, trims it, and treats a blank value as absent. There's no flag and no fallback to git config, EMAIL or any other variable.
  • applyDotEnv skips the key, and cli.ts is the CLI's only .env loader. I grepped for other readFileSync(.env) and dotenv uses and found none.
  • The value goes only into the submitFeedback POST body. It's not in trackRenderFeedback (the test pins this), not in the doctor or env summary (pinned too), not in fileGithubIssue (that function never receives it), and not printed.
  • Two places iterate over process.env. render/plan.ts resolveHfEnvOverrides reports names only. studioServer injects only VITE_STUDIO_*. Neither exposes the value.
  • Docs cover it in both cli.mdx and the feedback guide: never a flag, never from .env, not telemetry, unverified.

--source

  • Allowed values are exactly person and agent, trimmed and case-sensitive (Person is refused). When the flag is omitted the source is undefined, and JSON.stringify drops it, so the body stays byte-identical to today's. A bad value prints Source must be "person" or "agent" and calls failCommand() before shouldTrack(), so nothing is sent with telemetry on or off.

Server ignores the new fields (verified)

  • EF master, heygen/services/heygen_server/hyperframes/dto/feedback_dto.py: HyperframesFeedbackRequest is a plain pydantic BaseModel with no model_config, so extra defaults to ignore. The route (render_routes.py, submit_feedback) passes only rating, comment, cli_version, env and rating_scale to forward_feedback. So the new fields are dropped silently, with no 422.

Agent detection

  • --source doesn't default from detectAgentRuntime(), and nothing checks the two against each other, so an agent can send --source person. That's acceptable because it's self-declared, like the comment. The forwarded env string also doesn't carry agent_runtime (only client=), so the backend can't cross-check from the report alone.

Tests: 22/22 pass locally across the four files (after bun install and building parsers and core). Of 11 mutations, 10 were caught: accepting any source, defaulting to agent, case-folding, falling back to other variables, dropping the trim, dropping the .env skip, dropping the refusal, email in telemetry, dropping the cap, and dropping source from the body. The survivor was a console.log(feedbackEmail()) after submit, because no test asserts the email stays out of stdout. The code doesn't print it, so that's a test gap only.

CI is still running. Two non-required checks are red, both because the existing .env loader moved into a new file:

  • Comments: dotEnv.ts is at 24.2% comments and feedbackSource.ts at 20.0%, against the package's 13.1%. The dotEnv comments are the original loader's, moved verbatim.
  • Fallow audit: applyDotEnv has cognitive complexity 22, over the threshold of 15. That's the old loop, unchanged.

Non-blocking

  1. The agent nudge in telemetry/feedback.ts still suggests hyperframes feedback --rating <0-10> --comment "..." without --source agent, so the most common agent-filed reports will stay unlabeled. Adding --source agent there (or defaulting from detectAgentRuntime() when the flag is absent) would make the split useful sooner.
  2. --source with --search-miss is not validated: a bad value is accepted and ignored. The docs say a search-miss carries no source, so this is harmless, but it isn't refused the way it is on a rating.
  3. The PR body says the env-only rule means an agent can't attach an email "through a command line". An agent with a shell can still run HYPERFRAMES_FEEDBACK_EMAIL=x hyperframes feedback, and an agent the app launches inherits the variable if the app sets it in the agent's environment. The docs already say "not verified", which is the right contract. The app side should set the variable only on the feedback-button invocation, not in the agent's environment.
  4. On every render, resolveHfEnvOverrides will list HYPERFRAMES_FEEDBACK_EMAIL (name only) as an operator override when an app sets it. That's not PII, just noise in that signal; consider adding it to CLI_INTERNAL_HF_ENV_KEYS.
  5. The submitFeedback.ts comment says the caps "match the backend DTO", but the backend has no email field yet. The 254 cap should land in the DTO with the server change.

— Rames

@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Edit accuracy: accurate 1556 (base branch 1556), smooth 1414 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

Unstable (1)

  • crop-spin-px-r30-nested-z100-on: tracking 0.04, pressJump 0, drop 0.07, reload 0.07, render 0.22, renderKey 0.22, undo false, teleport true / tracking 0.04, pressJump 0, drop 0.07, reload 0.07, render 0.22, renderKey 0.22, undo true, teleport true / tracking 0.04, pressJump 0, drop 0.07, reload 0.07, render 0.22, renderKey 0.22, undo true, teleport true

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-approving at 09b87c95. The delta since my approval at c484e971 is two commits on top of it (3 files, +28 −31), and it changes no behavior I can find.

.env loader split (dotEnv.ts)

  • entryOf and valueOf are the old loop body, moved out unchanged: comment and blank lines skipped, export stripped, eq < 1 refused, a quoted value ends at its closing quote, an unterminated quote strips only the opener, and an unquoted # starts a comment only after whitespace. The feedback-email skip and the never-overwrite rule stay in applyDotEnv.
  • To check this, I ran the c484e971 loader next to this one on 200,000 seeded random .env files. The files were built from fragments that hit each branch: quotes, export, =/==, pass#word, #c, \r, an empty key, and HYPERFRAMES_FEEDBACK_EMAIL with and without a preset key. The resulting env objects were identical every time.

cli.ts

  • The import("./utils/dotEnv.js") moved out of the try, so a missing or broken loader module now throws instead of being swallowed. That part is right. The commit title also says "only a missing .env is ignored", but the catch {} still swallows every error from readFileSync (EACCES, EISDIR) and from applyDotEnv, the same as before. That's not a regression, but the title promises more than the code does. If that was the intent, if (code !== "ENOENT") throw in the catch would make it true.

Docs and comments: the doc comments on the FeedbackSource type and FEEDBACK_EMAIL_ENV were folded into the feedbackEmail doc, which still says it is never a flag and never read from a project .env, and is unverified.

Tests and CI: dotEnv, feedbackSource, submitFeedback and the feedback command tests pass, 22/22. Comments and Fallow audit are now green with no ignores added. At the time I checked, other checks were still pending and none had failed.

My earlier non-blocking notes still stand unchanged (agent nudge without --source agent, --search-miss not validating --source, the email name in hfEnvOverrides, no backend email cap yet).

— Rames

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit 0ca6947 Oct 4, 2026
96 checks passed
@miguel-heygen
miguel-heygen deleted the feat/cli-feedback-source-email branch October 4, 2026 05:18
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