Skip to content

Claude/trusting banzai c5f8f0 - #19

Open
sxnnywu wants to merge 5 commits into
mainfrom
claude/trusting-banzai-c5f8f0
Open

sxnnywu wants to merge 5 commits into
mainfrom
claude/trusting-banzai-c5f8f0

Conversation

@sxnnywu

@sxnnywu sxnnywu commented May 10, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

sebtsang and others added 3 commits May 9, 2026 23:00
Adds a CompanyProfile record (one row per founder) that grounds every
persona reasoning about a customer. Until now Qualifier scored fitScore
against an unknown ICP, Strategist picked angles without positioning,
and Writer drafted about a product the LLM had to guess at — the only
way to convey company info was stuffing it into the prompt textarea on
every run.

- New /settings/company page with a structured form (companyName,
  oneLiner, productDescription, ICP, positioning, voiceTone, valueProps,
  competitors, sourceUrl) plus an "Auto-fill from website" button that
  scrapes /, /about, /pricing, /product, /features via cheerio +
  turndown and runs an LLM drafter to populate the form for review.
- Workflow-start guard: POST /api/runs returns 409 with missingFields
  + settingsUrl until companyName, oneLiner, productDescription, and
  icp are non-empty. Frontend toasts with an "Open settings" action.
- Selective per-persona threading via pickCompanyProfileSlice — only
  the fields each persona's prompt actually references are spliced in.
  Operational personas (CRM Logger, Slack Digest, Pipeline Reporter,
  Linear Filer, Scheduler) get nothing, so their token budgets stay
  lean.
- WorkContext.summary now leads with the founder's company so the
  Conductor + Managers reason about tasks already grounded in who the
  company is — no Conductor code changes needed.
- 8 persona prompts updated to reference input.companyProfile.* with
  concrete grounding instructions (e.g. Qualifier scores against
  companyProfile.icp, Writer pairs companyProfile.voiceTone with the
  existing voice samples).
- Setup wizard collects an optional homepage URL and points the founder
  at /settings/company after install.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a collapsible Company Profile card between ResumePill and PromptInput.
Shows the founder at-a-glance whether their profile is complete (the four
required fields personas read) and renders the saved fields when it is.
Closes the loop with the workflow-start guard at /api/runs:

- Empty/incomplete state: amber "N missing" chip + warning banner naming the
  missing fields, "Open settings" CTA pointing at /settings/company. Same
  field labels the 409 error returns.
- Complete state: emerald "complete" chip + summary chip showing
  companyName + oneLiner. Expanded view renders one-liner / product /
  ICP / positioning / value props / competitors / voice-tone / source URL.
  Long markdown fields collapse to 4 lines with "Show more".

Also:
- /api/mock/company-profile mirror so the card works under
  NEXT_PUBLIC_USE_MOCKS=1 (returns makeMockCompanyProfile()).
- /settings/company now reads GMAESTRO_COMPANY_URL set by the setup wizard
  and passes it to CompanyProfileForm as setupUrl, so first-time founders
  see their homepage URL pre-filled in the Auto-fill input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Single source of truth for everything front+back share about the profile:
the slice map, the field labels, the per-field length caps, and the canonical
render order all move to lib/shared/company-profile-meta.ts. workflows.ts
re-imports the slice map; types.ts re-exports REQUIRED_COMPANY_PROFILE_FIELDS
for legacy callers.

Behavior changes (founder-visible):

- Settings form renders from the meta — required fields always come first
  (companyName → oneLiner → productDescription → icp), then optionals in a
  consistent order with the dashboard card.
- Each field shows "READ BY <persona>·<persona>·…" derived from
  pickCompanyProfileSlice — operational personas (Scheduler, CRM Logger
  etc.) are absent because they don't read the profile.
- Per-field counters: companyName/oneLiner show N/140, valueProps and
  competitors show N/8 with red highlighting + inline message when over,
  Save button disables when any field exceeds its Zod cap. Stops the
  founder from submitting payloads the server will 400.
- Profile-incomplete banner uses human labels ("company name, one-liner,
  product description, ICP") instead of camelCase identifiers.
- Partial-save toast names the still-missing fields: "Saved. Still missing:
  one-liner, ICP." (was: "Saved (2 required field(s) still empty).").
- Dashboard card sections render in the same order as the settings form,
  show the same field labels ("WHAT THE PRODUCT DOES" etc), and carry the
  same READ BY tags.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 10, 2026 04:08
@polarityinc

polarityinc Bot commented May 10, 2026

Copy link
Copy Markdown

Paragon Review Skipped

Hi @sxnnywu! Your Polarity credit balance is insufficient to complete this review.

Please visit https://app.paragon.run to finish your review.

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.

Pull request overview

Adds a founder “Company profile” feature that is required to run workflows, surfaces it in the dashboard/settings UI, and threads a persona-specific slice of the profile into downstream persona inputs. Also introduces an auto-fill flow that scrapes a company website and uses an LLM to draft the profile fields.

Changes:

  • Add company profile persistence (SQLite table + Drizzle schema/migration) and CRUD/scrape API routes, including a workflow-start guard that returns 409 until required fields are filled.
  • Add UI for viewing/editing the company profile (settings page + dashboard card + form) and update nav/runs UX to route users to settings when blocked.
  • Thread input.companyProfile slices into persona inputs and update prompts/registry accordingly; add a deterministic scraper + LLM drafter pipeline.

Reviewed changes

Copilot reviewed 32 out of 34 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks new dependencies for scraping/HTML→markdown conversion and related transitive deps.
package.json Adds cheerio, turndown, and @types/turndown.
lib/ui/hooks/use-company-profile.ts Client hook to load/hydrate company profile and compute completeness/missing fields.
lib/ui/components/top-nav.tsx Adds “Company” settings link in top nav.
lib/ui/components/prompt-input.tsx Handles 409 “company profile required” by showing toast + linking to settings.
lib/ui/components/company-profile-form.tsx New settings form with validation, save, and scrape auto-fill UX.
lib/ui/components/company-profile-card.tsx New dashboard card summarizing profile + completeness and linking to settings.
lib/state/workflows.ts Injects persona-specific companyProfile slices into task inputs/envelopes.
lib/state/work-context.ts Adds companyProfile into work context and includes formatted profile in summary.
lib/state/schema.ts Adds company_profiles table definition and exported types.
lib/state/company-profile.ts Implements profile get/upsert + completeness check + prompt formatting helper.
lib/shared/types.ts Adds CompanyProfile interface and re-exports required-field list.
lib/shared/schemas.ts Adds Zod schemas for company profile read/update and scrape request.
lib/shared/mocks.ts Adds mock company profile fixture.
lib/shared/company-profile-meta.ts Centralizes profile field metadata, ordering, required keys, and persona slice mapping.
lib/personas/registry.ts Extends persona input schema to accept optional companyProfile record.
lib/personas/prompts/writer.md Documents company profile grounding for Writer persona.
lib/personas/prompts/theme-synthesizer.md Documents company profile grounding for Theme Synthesizer persona.
lib/personas/prompts/strategist.md Documents company profile grounding for Strategist persona.
lib/personas/prompts/researcher.md Documents company profile grounding for Researcher persona.
lib/personas/prompts/qualifier.md Documents company profile grounding for Qualifier persona.
lib/personas/prompts/feedback-tagger.md Documents company profile grounding for Feedback Tagger persona.
lib/personas/prompts/brief-writer.md Documents company profile grounding for Brief Writer persona.
lib/personas/prompts/activation.md Documents company profile grounding for Activation persona.
lib/ingest/scrape.ts Implements deterministic multi-page scraping + HTML→markdown conversion.
lib/ingest/draft-profile.ts Adds LLM drafter to turn scrape bundle into a partial profile draft.
drizzle/migrations/0003_company_profile.sql Adds migration for company_profiles table.
bin/gmaestro.ts Captures optional company URL during setup and prints updated next steps.
app/api/runs/route.ts Adds pre-run guard that blocks runs (409) until profile required fields are filled.
app/api/mock/company-profile/route.ts Adds mock profile API for mock mode.
app/api/company-profile/scrape/route.ts Adds scrape+draft API endpoint for auto-fill.
app/api/company-profile/route.ts Adds GET/PUT API for company profile.
app/(dashboard)/settings/company/page.tsx Adds company profile settings page and uses setup URL fallback.
app/(dashboard)/page.tsx Shows company profile card on dashboard.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/shared/schemas.ts
Comment on lines +431 to +432
* unchanged; passing `null` clears a field. Soft caps mirror the form's
* help text — over-long values are still accepted but get truncated.
Comment on lines +22 to +33
const parsed = CompanyProfileScrapeRequestSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json(
{ error: "Invalid request", issues: parsed.error.issues },
{ status: 400 },
);
}

let bundle;
try {
bundle = await scrapeCompanySite(parsed.data.url);
} catch (err) {
Comment thread lib/ingest/scrape.ts
Comment on lines +67 to +70
const pages = await Promise.all(
WELL_KNOWN_PATHS.map((p) => fetchAndConvert(origin, p, deadline)),
);

Comment thread lib/ingest/scrape.ts
Comment on lines +188 to +192
const finalUrl = new URL(res.url);
const requested = new URL(url);
const redirectedOffOrigin = finalUrl.origin !== requested.origin;
const body = redirectedOffOrigin ? "" : await res.text();
return { status: res.status, body, redirectedOffOrigin };

- **`input.leadId`** — the lead's local id (e.g. `seed-lead-001`).
- **`input.item`** — the lead's record from the founder's local store. Always carries `email`, `name`, `company`, `source` (one of `inbound_form`, `trial_signup`, `manual_import`), and `rawMessage` (the lead's actual inbound text — usually the most useful single field for personalization).
- **`input.companyProfile.{companyName, oneLiner, productDescription, voiceTone}`** — **the founder's own company**. `oneLiner` and `productDescription` tell you what we sell so you can frame the hook around it without inventing. `voiceTone` complements the few-shot voice samples — concrete tone guidance the founder has written down (e.g. "lowercase-first, no hype words, signs as the founder"). Always present.
sxnnywu and others added 2 commits May 10, 2026 00:28
…i-c5f8f0

# Conflicts:
#	app/(dashboard)/page.tsx
#	lib/shared/mocks.ts
#	lib/shared/schemas.ts
#	lib/state/schema.ts
After the pivot to G-stack for blogs landed (PR #18, then merged into this
branch via 94d5f86), the 6 new content personas (linkedin/x/reddit-researcher,
synthesizer, blog-writer, blog-designer) had no entries in
COMPANY_PROFILE_SLICES — meaning the dispatcher was passing them empty
companyProfile slices. Each slice is tailored to what the persona reads:

- linkedin-researcher / x-researcher / reddit-researcher: companyName,
  oneLiner, productDescription, icp/competitors (icp for B2B-shaped sites
  like LinkedIn/Reddit, competitors for X trend research)
- synthesizer: full picture except value props/competitors — needs
  positioning + voiceTone to write an outline that sounds like ours
- blog-writer: voice-critical persona, full slice except sourceUrl
- blog-designer: just companyName + oneLiner for attribution; brand
  identity (logo, colors) is future work

PERSONAS_BY_FIELD will pick this up automatically (it iterates the slice
map at module load), so the form's "Read by X, Y, Z" attribution updates
without further changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

3 participants