diff --git a/apps/applyflow/src/components/dashboard/career-chat-workspace-recovery.test.tsx b/apps/applyflow/src/components/dashboard/career-chat-workspace-recovery.test.tsx
index 9f48fbe9..0f4c672f 100644
--- a/apps/applyflow/src/components/dashboard/career-chat-workspace-recovery.test.tsx
+++ b/apps/applyflow/src/components/dashboard/career-chat-workspace-recovery.test.tsx
@@ -12,6 +12,7 @@ import {
CareerChatWorkspaceView,
EMPTY_SPECIALIST_FIELDS,
} from "./career-chat-workspace";
+import { EMPTY_CAREER_PILOT_SIMPLE_INPUTS } from "./career-pilot-simple-inputs";
import * as careerChatClient from "./career-chat-workspace-client";
const bundle = createCareerBundle([
@@ -156,6 +157,8 @@ describe("CareerChatWorkspace retry flow", () => {
it("preserves form data, shows the error panel, and completes after retry", async () => {
const user = userEvent.setup();
+ const resumeText =
+ "Desenvolvi APIs REST em Node.js com TypeScript. Reduzi deploy em 30% com pipelines CI/CD.";
const runSpy = vi
.spyOn(careerChatClient, "runCareerChatLibrechat")
.mockRejectedValueOnce(new careerChatClient.CareerChatRequestError())
@@ -168,17 +171,13 @@ describe("CareerChatWorkspace retry flow", () => {
availableSignals={[]}
pilotPresentation
pilotIntent="analyze_resume"
- initialSpecialistFields={{
- ...EMPTY_SPECIALIST_FIELDS,
- resumeBullets: "Built APIs",
- resumeSkills: "TypeScript",
- }}
+ simpleInputs={{ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS, resumeText }}
+ onSimpleInputsChange={() => undefined}
/>,
);
const panel = within(view.getByTestId("career-chat-workspace-panel"));
- const bullets = panel.getByTestId("career-chat-resume-bullets") as HTMLTextAreaElement;
- const skills = panel.getByTestId("career-chat-resume-skills") as HTMLInputElement;
+ const resume = panel.getByTestId("career-pilot-resume-text") as HTMLTextAreaElement;
await user.click(panel.getByTestId("career-chat-consent-checkbox"));
await user.click(panel.getByTestId("career-chat-send-button"));
@@ -187,8 +186,7 @@ describe("CareerChatWorkspace retry flow", () => {
expect(panel.getByTestId("career-pilot-analysis-error")).toBeTruthy();
});
expect(panel.queryByTestId("career-pilot-result-view")).toBeNull();
- expect(bullets.value).toBe("Built APIs");
- expect(skills.value).toBe("TypeScript");
+ expect(resume.value).toBe(resumeText);
await user.click(panel.getByTestId("career-pilot-analysis-retry"));
@@ -197,8 +195,7 @@ describe("CareerChatWorkspace retry flow", () => {
});
expect(runSpy).toHaveBeenCalledTimes(2);
- expect(bullets.value).toBe("Built APIs");
- expect(skills.value).toBe("TypeScript");
+ expect(resume.value).toBe(resumeText);
expect(panel.queryByTestId("career-pilot-analysis-error")).toBeNull();
});
});
diff --git a/apps/applyflow/src/components/dashboard/career-chat-workspace.test.tsx b/apps/applyflow/src/components/dashboard/career-chat-workspace.test.tsx
index 5da81579..607d378e 100644
--- a/apps/applyflow/src/components/dashboard/career-chat-workspace.test.tsx
+++ b/apps/applyflow/src/components/dashboard/career-chat-workspace.test.tsx
@@ -432,6 +432,22 @@ describe("buildSpecialistAnalysisInput", () => {
]);
});
+ it("extracts Unicode keywords for Portuguese job requirements", () => {
+ const input = buildSpecialistAnalysisInput({
+ action: "analyze_ats_compatibility",
+ fields: {
+ ...EMPTY_SPECIALIST_FIELDS,
+ jobRequirements: "Experiência com backend\nInglês intermediário\nConhecimento em cloud",
+ },
+ mainStack: ["TypeScript"],
+ fallbackRole: "Backend Engineer",
+ });
+ expect(input?.jobSnapshot?.keywords).toContain("experiência");
+ expect(input?.jobSnapshot?.keywords).toContain("inglês");
+ expect(input?.jobSnapshot?.keywords).toContain("intermediário");
+ expect(input?.jobSnapshot?.keywords).toContain("conhecimento");
+ });
+
it("builds target roles for plan_career_strategy", () => {
const input = buildSpecialistAnalysisInput({
action: "plan_career_strategy",
diff --git a/apps/applyflow/src/components/dashboard/career-chat-workspace.tsx b/apps/applyflow/src/components/dashboard/career-chat-workspace.tsx
index db4ebb0f..e810e495 100644
--- a/apps/applyflow/src/components/dashboard/career-chat-workspace.tsx
+++ b/apps/applyflow/src/components/dashboard/career-chat-workspace.tsx
@@ -84,6 +84,20 @@ import {
import { buildCareerPilotResultModel } from "./career-pilot-result-mapper";
import { CareerPilotResultView } from "./career-pilot-result-view";
import { CareerPilotFeedback } from "./career-pilot-feedback";
+import { CareerPilotInputReview } from "./career-pilot-input-review";
+import { CareerPilotSimpleInputsForm } from "./career-pilot-simple-inputs-form";
+import {
+ buildCareerSpecialistFieldsFromSimpleInputs,
+ extractJobKeywords,
+ hasSimplePilotAnalysisInputs,
+} from "./career-pilot-input-normalizer";
+import {
+ CAREER_PILOT_EMPTY_CAREER_GOAL_MESSAGE,
+ CAREER_PILOT_EMPTY_JOB_MESSAGE,
+ CAREER_PILOT_EMPTY_RESUME_MESSAGE,
+} from "./career-pilot-simple-input-content";
+import type { CareerPilotSimpleInputs } from "./career-pilot-simple-inputs";
+import { EMPTY_CAREER_PILOT_SIMPLE_INPUTS } from "./career-pilot-simple-inputs";
import { CareerAnalysisLoading } from "./career-analysis-loading";
import { CareerAnalysisError } from "./career-analysis-error";
import {
@@ -174,12 +188,7 @@ export function buildSpecialistAnalysisInput(input: {
jobSnapshot: {
title: fallbackRole || "Target role",
requiredRequirements: requirements,
- keywords: requirements.flatMap((req) =>
- req
- .toLowerCase()
- .split(/[^a-z0-9+#.]+/)
- .filter((token) => token.length >= 3),
- ),
+ keywords: extractJobKeywords(requirements.join(" ")),
},
};
}
@@ -197,6 +206,8 @@ export type CareerChatWorkspaceProps = {
pilotPresentation?: boolean;
pilotIntent?: CareerChatIntent;
initialSpecialistFields?: CareerSpecialistFields;
+ simpleInputs?: CareerPilotSimpleInputs;
+ onSimpleInputsChange?: (next: CareerPilotSimpleInputs) => void;
onPilotActionChange?: (action: CareerChatIntent) => void;
onPilotAnalysisComplete?: (intent: CareerPilotIntent) => void;
};
@@ -273,6 +284,9 @@ export function CareerChatWorkspaceView({
onSubmitPilotFeedback,
submitDisabled = false,
onDismissError,
+ simpleInputs,
+ onSimpleInputsChange,
+ validationMessage = null,
}: CareerChatWorkspaceProps & {
action: CareerChatIntent;
message: string;
@@ -303,6 +317,9 @@ export function CareerChatWorkspaceView({
}) => Promise<{ ok: boolean }>;
submitDisabled?: boolean;
onDismissError?: () => void;
+ simpleInputs?: CareerPilotSimpleInputs;
+ onSimpleInputsChange?: (next: CareerPilotSimpleInputs) => void;
+ validationMessage?: string | null;
}) {
const messageLength = message.length;
const showSpecialist = pilotPresentation || isSpecialistIntent(action);
@@ -319,8 +336,9 @@ export function CareerChatWorkspaceView({
const visibleActions = pilotPresentation ? CAREER_PILOT_INTENTS : (Object.keys(CAREER_CHAT_WORKSPACE_ACTION_LABELS) as CareerChatIntent[]);
const actionLabels = pilotPresentation ? CAREER_PILOT_ACTION_LABELS : CAREER_CHAT_WORKSPACE_ACTION_LABELS;
- const pilotEmptyHint =
- action === "plan_career_strategy"
+ const pilotEmptyHint = validationMessage
+ ? validationMessage
+ : action === "plan_career_strategy"
? CAREER_PILOT_EMPTY_STRATEGY_HINT
: action === "analyze_ats_compatibility"
? CAREER_PILOT_EMPTY_ATS_HINT
@@ -458,21 +476,32 @@ export function CareerChatWorkspaceView({
)}
- {showSpecialist ? (
+ {showSpecialist && pilotPresentation && simpleInputs && onSimpleInputsChange ? (
- {!pilotPresentation ? (
-
- {CAREER_CHAT_WORKSPACE_SPECIALIST_LABEL}
-
- ) : null}
+
+
onSpecialistFieldChange?.(field, value)}
+ />
+
+ ) : null}
+
+ {showSpecialist && !pilotPresentation ? (
+
+
+ {CAREER_CHAT_WORKSPACE_SPECIALIST_LABEL}
+
{action !== "plan_career_strategy" ? (
<>
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.test.ts b/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.test.ts
new file mode 100644
index 00000000..d7909348
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.test.ts
@@ -0,0 +1,182 @@
+import { describe, expect, it } from "vitest";
+import {
+ buildCareerSpecialistFieldsFromSimpleInputs,
+ extractJobKeywords,
+ extractJobRequirements,
+ extractLikelySkills,
+ extractResumeLines,
+ hasSimplePilotAnalysisInputs,
+ normalizeJobDescription,
+ normalizeResumeText,
+ PILOT_SKILL_CATALOG,
+} from "./career-pilot-input-normalizer";
+import { EMPTY_CAREER_PILOT_SIMPLE_INPUTS } from "./career-pilot-simple-inputs";
+
+describe("normalizeResumeText", () => {
+ it("normalizes line breaks and removes repeated empty lines", () => {
+ expect(normalizeResumeText("Linha 1\r\n\r\n\r\nLinha 2")).toBe("Linha 1\n\nLinha 2");
+ });
+
+ it("returns empty string for blank input", () => {
+ expect(normalizeResumeText(" \n ")).toBe("");
+ });
+});
+
+describe("extractResumeLines", () => {
+ it("extracts multiple lines from a multiline resume", () => {
+ const lines = extractResumeLines("Experiência A\n• Resultado B\n\nResultado C");
+ expect(lines.length).toBeGreaterThanOrEqual(2);
+ expect(lines[0]).toContain("Experiência");
+ });
+
+ it("splits single-block text into sentences when needed", () => {
+ const lines = extractResumeLines(
+ "Desenvolvi APIs REST em Node.js. Reduzi tempo de deploy em 30% com CI/CD.",
+ );
+ expect(lines.length).toBeGreaterThanOrEqual(1);
+ });
+});
+
+describe("extractLikelySkills", () => {
+ it("identifies skills present in the text", () => {
+ const skills = extractLikelySkills("Experiência com TypeScript, React e Node.js em produção.");
+ expect(skills).toContain("TypeScript");
+ expect(skills).toContain("React");
+ expect(skills).toContain("Node.js");
+ });
+
+ it("does not invent skills absent from the text", () => {
+ const skills = extractLikelySkills("Experiência com planilhas e atendimento ao cliente.");
+ for (const skill of skills) {
+ expect(PILOT_SKILL_CATALOG).toContain(skill);
+ }
+ expect(skills).not.toContain("Kubernetes");
+ });
+
+ it("removes duplicate skills", () => {
+ const skills = extractLikelySkills("TypeScript TypeScript typescript e React.");
+ const lowered = skills.map((s) => s.toLowerCase());
+ expect(new Set(lowered).size).toBe(lowered.length);
+ });
+});
+
+describe("extractJobRequirements", () => {
+ it("extracts requirements from multiline job descriptions", () => {
+ const requirements = extractJobRequirements(
+ "Requisitos:\nExperiência com TypeScript\nInglês intermediário",
+ );
+ expect(requirements.some((line) => line.includes("TypeScript"))).toBe(true);
+ expect(requirements.some((line) => line.includes("intermediário"))).toBe(true);
+ });
+});
+
+describe("extractJobKeywords", () => {
+ it("preserves accented Portuguese tokens", () => {
+ const keywords = extractJobKeywords(
+ "experiência com backend, inglês intermediário e conhecimento em cloud",
+ );
+ expect(keywords).toContain("experiência");
+ expect(keywords).toContain("inglês");
+ expect(keywords).toContain("intermediário");
+ expect(keywords).toContain("conhecimento");
+ expect(keywords).not.toContain("experi");
+ expect(keywords).not.toContain("ingl");
+ });
+
+ it("returns empty array for blank text", () => {
+ expect(extractJobKeywords("")).toEqual([]);
+ });
+});
+
+describe("buildCareerSpecialistFieldsFromSimpleInputs", () => {
+ it("maps simple resume inputs to specialist fields", () => {
+ const fields = buildCareerSpecialistFieldsFromSimpleInputs(
+ {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ targetRole: "Desenvolvedor Full Stack",
+ resumeText:
+ "Desenvolvi APIs em Node.js com TypeScript.\nReduzi deploy em 30% com CI/CD no GitHub Actions.",
+ },
+ "analyze_resume",
+ );
+ expect(fields.resumeBullets).toContain("Node.js");
+ expect(fields.resumeSkills).toContain("TypeScript");
+ expect(fields.targetRoles).toBe("Desenvolvedor Full Stack");
+ });
+
+ it("maps job description to requirements for ATS flow", () => {
+ const fields = buildCareerSpecialistFieldsFromSimpleInputs(
+ {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ resumeText: "Experiência com TypeScript e Node.js em APIs REST para SaaS.",
+ jobDescription: "Requisitos:\nExperiência com TypeScript\nInglês intermediário",
+ },
+ "analyze_ats_compatibility",
+ );
+ expect(fields.jobRequirements).toContain("TypeScript");
+ expect(fields.jobRequirements).toContain("intermediário");
+ });
+
+ it("maps career plan inputs to target roles and availability", () => {
+ const fields = buildCareerSpecialistFieldsFromSimpleInputs(
+ {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ careerGoal: "Vaga Full Stack Sênior em 90 dias",
+ weeklyAvailability: "10 horas por semana",
+ constraints: "Remoto, CLT",
+ },
+ "plan_career_strategy",
+ );
+ expect(fields.targetRoles).toContain("Full Stack");
+ expect(fields.availability).toContain("10 horas");
+ expect(fields.availability).toContain("Remoto");
+ });
+});
+
+describe("hasSimplePilotAnalysisInputs", () => {
+ it("requires sufficient resume text for analyze_resume", () => {
+ expect(
+ hasSimplePilotAnalysisInputs("analyze_resume", {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ resumeText: "curto",
+ }),
+ ).toBe(false);
+ expect(
+ hasSimplePilotAnalysisInputs("analyze_resume", {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ resumeText:
+ "Desenvolvi APIs REST em Node.js com TypeScript para integração com parceiros.",
+ }),
+ ).toBe(true);
+ });
+
+ it("requires job description for ATS comparison", () => {
+ expect(
+ hasSimplePilotAnalysisInputs("analyze_ats_compatibility", {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ resumeText:
+ "Desenvolvi APIs REST em Node.js com TypeScript para integração com parceiros.",
+ jobDescription: "curta",
+ }),
+ ).toBe(false);
+ });
+
+ it("requires career goal for strategy plan", () => {
+ expect(
+ hasSimplePilotAnalysisInputs("plan_career_strategy", EMPTY_CAREER_PILOT_SIMPLE_INPUTS),
+ ).toBe(false);
+ expect(
+ hasSimplePilotAnalysisInputs("plan_career_strategy", {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ careerGoal: "Conseguir vaga sênior",
+ }),
+ ).toBe(true);
+ });
+});
+
+describe("normalizeJobDescription", () => {
+ it("respects maximum length", () => {
+ const long = "a".repeat(20_000);
+ expect(normalizeJobDescription(long).length).toBeLessThanOrEqual(12_000);
+ });
+});
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.ts b/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.ts
new file mode 100644
index 00000000..531f0180
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-input-normalizer.ts
@@ -0,0 +1,202 @@
+import type { CareerChatIntent } from "@devflow/career-core";
+import type { CareerSpecialistFields } from "./career-chat-workspace";
+import { isCareerPilotIntent, type CareerPilotIntent } from "./career-pilot-content";
+import {
+ type CareerPilotSimpleInputs,
+ MAX_PILOT_JOB_DESCRIPTION_LENGTH,
+ MAX_PILOT_RESUME_TEXT_LENGTH,
+ MIN_PILOT_JOB_DESCRIPTION_LENGTH,
+ MIN_PILOT_RESUME_TEXT_LENGTH,
+} from "./career-pilot-simple-inputs";
+
+export const PILOT_SKILL_CATALOG = [
+ "JavaScript",
+ "TypeScript",
+ "React",
+ "Next.js",
+ "Node.js",
+ "Express",
+ "NestJS",
+ "Prisma",
+ "PostgreSQL",
+ "MySQL",
+ "MongoDB",
+ "Redis",
+ "AWS",
+ "Azure",
+ "GCP",
+ "Docker",
+ "Kubernetes",
+ "Jest",
+ "Vitest",
+ "Playwright",
+ "Cypress",
+ "Git",
+ "GitHub Actions",
+ "REST",
+ "GraphQL",
+] as const;
+
+const MAX_LINES = 50;
+const MAX_LINE_LENGTH = 500;
+
+export function normalizeResumeText(value: string): string {
+ return value
+ .replace(/\r\n/g, "\n")
+ .replace(/\r/g, "\n")
+ .replace(/\n{3,}/g, "\n\n")
+ .trim()
+ .slice(0, MAX_PILOT_RESUME_TEXT_LENGTH);
+}
+
+export function normalizeJobDescription(value: string): string {
+ return value
+ .replace(/\r\n/g, "\n")
+ .replace(/\r/g, "\n")
+ .replace(/\n{3,}/g, "\n\n")
+ .trim()
+ .slice(0, MAX_PILOT_JOB_DESCRIPTION_LENGTH);
+}
+
+export function extractResumeLines(resumeText: string): string[] {
+ const normalized = normalizeResumeText(resumeText);
+ if (!normalized) {
+ return [];
+ }
+
+ const lines = normalized
+ .split("\n")
+ .map((line) => line.replace(/^[\s•\-*]+/, "").trim())
+ .filter(Boolean)
+ .map((line) => line.slice(0, MAX_LINE_LENGTH));
+
+ if (lines.length > 1) {
+ return lines.slice(0, MAX_LINES);
+ }
+
+ return normalized
+ .split(/(?<=[.!?])\s+/)
+ .map((sentence) => sentence.trim())
+ .filter((sentence) => sentence.length >= 8)
+ .slice(0, MAX_LINES);
+}
+
+function escapeRegExp(value: string): string {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
+export function extractLikelySkills(text: string): string[] {
+ const normalized = normalizeResumeText(text);
+ if (!normalized) {
+ return [];
+ }
+
+ const haystack = normalized.toLowerCase();
+ const found: string[] = [];
+ const seen = new Set();
+
+ for (const skill of PILOT_SKILL_CATALOG) {
+ const pattern = new RegExp(`\\b${escapeRegExp(skill.toLowerCase())}\\b`, "i");
+ if (pattern.test(haystack) && !seen.has(skill.toLowerCase())) {
+ seen.add(skill.toLowerCase());
+ found.push(skill);
+ }
+ }
+
+ return found.slice(0, MAX_LINES);
+}
+
+export function extractJobRequirements(jobDescription: string): string[] {
+ const normalized = normalizeJobDescription(jobDescription);
+ if (!normalized) {
+ return [];
+ }
+
+ const lineCandidates = normalized
+ .split("\n")
+ .map((line) => line.replace(/^[\s•\-*\d.)]+/, "").trim())
+ .filter((line) => line.length >= 3);
+
+ if (lineCandidates.length >= 2) {
+ return lineCandidates.slice(0, MAX_LINES);
+ }
+
+ return normalized
+ .split(/(?<=[.!?])\s+/)
+ .map((sentence) => sentence.trim())
+ .filter((sentence) => sentence.length >= 8)
+ .slice(0, MAX_LINES);
+}
+
+export function extractJobKeywords(text: string): string[] {
+ const seen = new Set();
+ const keywords: string[] = [];
+
+ for (const token of text.toLowerCase().split(/[^\p{L}\p{N}+#.]+/u)) {
+ const trimmed = token.trim();
+ if (trimmed.length < 3 || seen.has(trimmed)) {
+ continue;
+ }
+ seen.add(trimmed);
+ keywords.push(trimmed);
+ }
+
+ return keywords.slice(0, 100);
+}
+
+function joinTargetRoles(goal: string): string {
+ const parts = goal
+ .split(/[\n,;]+/)
+ .map((entry) => entry.trim())
+ .filter(Boolean);
+ return parts.length > 0 ? parts.join(", ") : goal.trim();
+}
+
+export function buildCareerSpecialistFieldsFromSimpleInputs(
+ input: CareerPilotSimpleInputs,
+ intent: CareerChatIntent,
+): CareerSpecialistFields {
+ const resumeText = normalizeResumeText(input.resumeText);
+ const jobDescription = normalizeJobDescription(input.jobDescription);
+ const resumeLines = extractResumeLines(resumeText);
+ const skills = extractLikelySkills(resumeText);
+ const requirements = extractJobRequirements(jobDescription);
+ const availabilityParts = [input.weeklyAvailability.trim(), input.constraints.trim()].filter(Boolean);
+
+ if (isCareerPilotIntent(intent) && intent === "plan_career_strategy") {
+ return {
+ resumeBullets: resumeLines.join("\n"),
+ resumeSkills: skills.join(", "),
+ jobRequirements: requirements.join("\n"),
+ targetRoles: joinTargetRoles(input.careerGoal),
+ availability: availabilityParts.join(" · "),
+ };
+ }
+
+ return {
+ resumeBullets: resumeLines.join("\n"),
+ resumeSkills: skills.join(", "),
+ jobRequirements: requirements.join("\n"),
+ targetRoles: input.targetRole.trim(),
+ availability: availabilityParts.join(" · "),
+ };
+}
+
+export function hasSimplePilotAnalysisInputs(
+ action: CareerPilotIntent,
+ input: CareerPilotSimpleInputs,
+): boolean {
+ const resumeText = normalizeResumeText(input.resumeText);
+ const jobDescription = normalizeJobDescription(input.jobDescription);
+ const resumeOk = resumeText.length >= MIN_PILOT_RESUME_TEXT_LENGTH;
+
+ if (action === "plan_career_strategy") {
+ return input.careerGoal.trim().length > 0;
+ }
+
+ if (action === "analyze_ats_compatibility") {
+ return resumeOk && jobDescription.length >= MIN_PILOT_JOB_DESCRIPTION_LENGTH;
+ }
+
+ return resumeOk;
+}
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-input-review.tsx b/apps/applyflow/src/components/dashboard/career-pilot-input-review.tsx
new file mode 100644
index 00000000..a342632c
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-input-review.tsx
@@ -0,0 +1,132 @@
+"use client";
+
+import type { CareerChatIntent } from "@devflow/career-core";
+import type { CareerSpecialistFields } from "./career-chat-workspace";
+import { isCareerPilotIntent } from "./career-pilot-content";
+import {
+ CAREER_PILOT_REVIEW_AVAILABILITY_LABEL,
+ CAREER_PILOT_REVIEW_DISCLOSURE_HINT,
+ CAREER_PILOT_REVIEW_DISCLOSURE_TITLE,
+ CAREER_PILOT_REVIEW_EXPERIENCES_LABEL,
+ CAREER_PILOT_REVIEW_REQUIREMENTS_LABEL,
+ CAREER_PILOT_REVIEW_SKILLS_LABEL,
+ CAREER_PILOT_REVIEW_TARGET_ROLE_LABEL,
+} from "./career-pilot-simple-input-content";
+import { careerPolishInput, careerPolishLabel, careerPolishTextarea } from "./career-polish-classes";
+
+export function CareerPilotInputReview({
+ intent,
+ fields,
+ onFieldChange,
+}: {
+ intent: CareerChatIntent;
+ fields: CareerSpecialistFields;
+ onFieldChange: (field: keyof CareerSpecialistFields, value: string) => void;
+}) {
+ if (!isCareerPilotIntent(intent)) {
+ return null;
+ }
+
+ const showResume = intent !== "plan_career_strategy";
+ const showJob = intent === "analyze_ats_compatibility";
+ const showPlan = intent === "plan_career_strategy";
+
+ return (
+
+
+
+ {CAREER_PILOT_REVIEW_DISCLOSURE_TITLE}
+
+ ▾
+
+
+
+
+
{CAREER_PILOT_REVIEW_DISCLOSURE_HINT}
+
+ {showResume ? (
+ <>
+
+
+
+ );
+}
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-simple-input-content.ts b/apps/applyflow/src/components/dashboard/career-pilot-simple-input-content.ts
new file mode 100644
index 00000000..6abbb047
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-simple-input-content.ts
@@ -0,0 +1,43 @@
+export const CAREER_PILOT_SIMPLE_TARGET_ROLE_LABEL = "Cargo desejado";
+export const CAREER_PILOT_SIMPLE_TARGET_ROLE_HINT = "Exemplo: Desenvolvedor Full Stack";
+export const CAREER_PILOT_SIMPLE_TARGET_ROLE_OPTIONAL = "(opcional, mas recomendado)";
+
+export const CAREER_PILOT_SIMPLE_RESUME_LABEL = "Seu currículo";
+export const CAREER_PILOT_SIMPLE_RESUME_HINT =
+ "Cole aqui o texto do seu currículo. Você pode copiar o conteúdo de um PDF ou documento.";
+
+export const CAREER_PILOT_SIMPLE_JOB_LABEL = "Descrição da vaga";
+export const CAREER_PILOT_SIMPLE_JOB_HINT =
+ "Cole aqui a descrição completa da oportunidade.";
+
+export const CAREER_PILOT_SIMPLE_CAREER_GOAL_LABEL = "Objetivo profissional";
+export const CAREER_PILOT_SIMPLE_CAREER_GOAL_HINT =
+ "Exemplo: Conseguir uma vaga Full Stack Sênior nos próximos 90 dias.";
+
+export const CAREER_PILOT_SIMPLE_AVAILABILITY_LABEL = "Tempo disponível por semana";
+export const CAREER_PILOT_SIMPLE_AVAILABILITY_HINT = "Exemplo: 10 horas por semana.";
+
+export const CAREER_PILOT_SIMPLE_CONSTRAINTS_LABEL = "Preferências ou restrições";
+export const CAREER_PILOT_SIMPLE_CONSTRAINTS_HINT =
+ "Exemplo: Trabalho remoto, empresas SaaS e contratação CLT.";
+
+export const CAREER_PILOT_SIMPLE_INPUT_PRIVACY =
+ "O conteúdo é usado somente nesta análise e não é armazenado automaticamente.";
+
+export const CAREER_PILOT_REVIEW_DISCLOSURE_TITLE = "Revisar informações extraídas";
+export const CAREER_PILOT_REVIEW_DISCLOSURE_HINT =
+ "A revisão é opcional. Ajuste apenas se alguma informação tiver sido interpretada incorretamente.";
+
+export const CAREER_PILOT_REVIEW_EXPERIENCES_LABEL = "Experiências e resultados";
+export const CAREER_PILOT_REVIEW_SKILLS_LABEL = "Principais competências";
+export const CAREER_PILOT_REVIEW_REQUIREMENTS_LABEL = "Requisitos identificados";
+export const CAREER_PILOT_REVIEW_TARGET_ROLE_LABEL = "Cargo desejado";
+export const CAREER_PILOT_REVIEW_AVAILABILITY_LABEL = "Tempo disponível";
+
+export const CAREER_PILOT_EMPTY_RESUME_MESSAGE =
+ "Cole um trecho suficiente do currículo para realizar a análise.";
+
+export const CAREER_PILOT_EMPTY_JOB_MESSAGE = "Cole a descrição da vaga que deseja comparar.";
+
+export const CAREER_PILOT_EMPTY_CAREER_GOAL_MESSAGE =
+ "Informe seu objetivo profissional para montar o plano.";
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs-form.tsx b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs-form.tsx
new file mode 100644
index 00000000..4ee8ae19
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs-form.tsx
@@ -0,0 +1,163 @@
+"use client";
+
+import type { CareerChatIntent } from "@devflow/career-core";
+import { isCareerPilotIntent } from "./career-pilot-content";
+import {
+ CAREER_PILOT_SIMPLE_AVAILABILITY_HINT,
+ CAREER_PILOT_SIMPLE_AVAILABILITY_LABEL,
+ CAREER_PILOT_SIMPLE_CAREER_GOAL_HINT,
+ CAREER_PILOT_SIMPLE_CAREER_GOAL_LABEL,
+ CAREER_PILOT_SIMPLE_CONSTRAINTS_HINT,
+ CAREER_PILOT_SIMPLE_CONSTRAINTS_LABEL,
+ CAREER_PILOT_SIMPLE_INPUT_PRIVACY,
+ CAREER_PILOT_SIMPLE_JOB_HINT,
+ CAREER_PILOT_SIMPLE_JOB_LABEL,
+ CAREER_PILOT_SIMPLE_RESUME_HINT,
+ CAREER_PILOT_SIMPLE_RESUME_LABEL,
+ CAREER_PILOT_SIMPLE_TARGET_ROLE_HINT,
+ CAREER_PILOT_SIMPLE_TARGET_ROLE_LABEL,
+ CAREER_PILOT_SIMPLE_TARGET_ROLE_OPTIONAL,
+} from "./career-pilot-simple-input-content";
+import type { CareerPilotSimpleInputs } from "./career-pilot-simple-inputs";
+import {
+ careerPolishBodyText,
+ careerPolishInput,
+ careerPolishLabel,
+ careerPolishResumeTextarea,
+ careerPolishJobTextarea,
+} from "./career-polish-classes";
+
+export function CareerPilotSimpleInputsForm({
+ intent,
+ value,
+ onChange,
+}: {
+ intent: CareerChatIntent;
+ value: CareerPilotSimpleInputs;
+ onChange: (next: CareerPilotSimpleInputs) => void;
+}) {
+ if (!isCareerPilotIntent(intent)) {
+ return null;
+ }
+
+ function patch(partial: Partial) {
+ onChange({ ...value, ...partial });
+ }
+
+ return (
+
+ {intent === "analyze_resume" ? (
+ <>
+
+
+
{CAREER_PILOT_SIMPLE_TARGET_ROLE_HINT}
+
patch({ targetRole: event.target.value })}
+ data-testid="career-pilot-target-role"
+ />
+
+
+
+
{CAREER_PILOT_SIMPLE_RESUME_HINT}
+
+ >
+ ) : null}
+
+ {intent === "analyze_ats_compatibility" ? (
+ <>
+
+
+
{CAREER_PILOT_SIMPLE_RESUME_HINT}
+
+
+
+
{CAREER_PILOT_SIMPLE_JOB_HINT}
+
+ >
+ ) : null}
+
+ {intent === "plan_career_strategy" ? (
+ <>
+
+
+
{CAREER_PILOT_SIMPLE_CAREER_GOAL_HINT}
+
+
+
+
{CAREER_PILOT_SIMPLE_AVAILABILITY_HINT}
+
patch({ weeklyAvailability: event.target.value })}
+ data-testid="career-pilot-weekly-availability"
+ />
+
+
+
+
{CAREER_PILOT_SIMPLE_CONSTRAINTS_HINT}
+
patch({ constraints: event.target.value })}
+ data-testid="career-pilot-constraints"
+ />
+
+ >
+ ) : null}
+
+
+ {CAREER_PILOT_SIMPLE_INPUT_PRIVACY}
+
+
+ );
+}
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.test.tsx b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.test.tsx
new file mode 100644
index 00000000..906bd51a
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.test.tsx
@@ -0,0 +1,117 @@
+// @vitest-environment jsdom
+import { describe, expect, it, beforeEach, afterEach } from "vitest";
+import { render, screen, cleanup, fireEvent } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { CareerPilotSimpleInputsForm } from "./career-pilot-simple-inputs-form";
+import { CareerPilotInputReview } from "./career-pilot-input-review";
+import { EMPTY_SPECIALIST_FIELDS } from "./career-chat-workspace";
+import {
+ CAREER_PILOT_REVIEW_DISCLOSURE_TITLE,
+ CAREER_PILOT_SIMPLE_INPUT_PRIVACY,
+ CAREER_PILOT_SIMPLE_JOB_LABEL,
+ CAREER_PILOT_SIMPLE_RESUME_LABEL,
+ CAREER_PILOT_SIMPLE_TARGET_ROLE_LABEL,
+} from "./career-pilot-simple-input-content";
+import { EMPTY_CAREER_PILOT_SIMPLE_INPUTS } from "./career-pilot-simple-inputs";
+
+describe("CareerPilotSimpleInputsForm", () => {
+ beforeEach(() => cleanup());
+ afterEach(() => cleanup());
+ it("shows Portuguese resume fields for analyze_resume", () => {
+ render(
+ undefined}
+ />,
+ );
+
+ expect(screen.getByText(CAREER_PILOT_SIMPLE_TARGET_ROLE_LABEL)).toBeTruthy();
+ expect(screen.getByText(CAREER_PILOT_SIMPLE_RESUME_LABEL)).toBeTruthy();
+ expect(screen.getByTestId("career-pilot-resume-text")).toBeTruthy();
+ expect(screen.getByText(CAREER_PILOT_SIMPLE_INPUT_PRIVACY)).toBeTruthy();
+ expect(screen.queryByText("Resume bullets")).toBeNull();
+ expect(screen.queryByText("Resume skills")).toBeNull();
+ });
+
+ it("shows job description field for ATS flow", () => {
+ render(
+ undefined}
+ />,
+ );
+
+ expect(screen.getByText(CAREER_PILOT_SIMPLE_JOB_LABEL)).toBeTruthy();
+ expect(screen.getByTestId("career-pilot-job-description")).toBeTruthy();
+ expect(screen.queryByText("Job requirements")).toBeNull();
+ });
+
+ it("calls onChange when resume text is edited", async () => {
+ const user = userEvent.setup();
+ let latest = EMPTY_CAREER_PILOT_SIMPLE_INPUTS;
+ render(
+ {
+ latest = next;
+ }}
+ />,
+ );
+
+ await user.type(screen.getByTestId("career-pilot-resume-text"), "Texto do currículo");
+ expect(latest.resumeText.length).toBeGreaterThan(0);
+ });
+});
+
+describe("CareerPilotInputReview", () => {
+ beforeEach(() => cleanup());
+ afterEach(() => cleanup());
+ it("renders closed disclosure with Portuguese labels", () => {
+ render(
+ undefined}
+ />,
+ );
+
+ const disclosure = screen.getByTestId("career-pilot-input-review") as HTMLDetailsElement;
+ expect(disclosure.open).toBe(false);
+ expect(screen.getByText(CAREER_PILOT_REVIEW_DISCLOSURE_TITLE)).toBeTruthy();
+ expect(screen.getByText("Experiências e resultados")).toBeTruthy();
+ expect(screen.getByText("Principais competências")).toBeTruthy();
+ });
+
+ it("updates structured fields when review inputs change", async () => {
+ const user = userEvent.setup();
+ let skills = "TypeScript";
+ render(
+ {
+ if (field === "resumeSkills") {
+ skills = value;
+ }
+ }}
+ />,
+ );
+
+ const disclosure = screen.getByTestId("career-pilot-input-review");
+ await user.click(disclosure.querySelector("summary")!);
+ const input = screen.getByTestId("career-pilot-review-skills") as HTMLInputElement;
+ fireEvent.change(input, { target: { value: "React" } });
+ expect(skills).toBe("React");
+ });
+});
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.ts b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.ts
new file mode 100644
index 00000000..e72554c6
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-simple-inputs.ts
@@ -0,0 +1,31 @@
+import type { CareerChatIntent } from "@devflow/career-core";
+import { isCareerPilotIntent, type CareerPilotIntent } from "./career-pilot-content";
+
+export type CareerPilotSimpleInputs = {
+ targetRole: string;
+ resumeText: string;
+ jobDescription: string;
+ careerGoal: string;
+ weeklyAvailability: string;
+ constraints: string;
+};
+
+export const EMPTY_CAREER_PILOT_SIMPLE_INPUTS: CareerPilotSimpleInputs = {
+ targetRole: "",
+ resumeText: "",
+ jobDescription: "",
+ careerGoal: "",
+ weeklyAvailability: "",
+ constraints: "",
+};
+
+export const MAX_PILOT_RESUME_TEXT_LENGTH = 12_000;
+export const MAX_PILOT_JOB_DESCRIPTION_LENGTH = 12_000;
+export const MIN_PILOT_RESUME_TEXT_LENGTH = 40;
+export const MIN_PILOT_JOB_DESCRIPTION_LENGTH = 20;
+
+export function isCareerPilotIntentForValidation(
+ intent: CareerChatIntent,
+): intent is CareerPilotIntent {
+ return isCareerPilotIntent(intent);
+}
diff --git a/apps/applyflow/src/components/dashboard/career-pilot-simplified-input.integration.test.tsx b/apps/applyflow/src/components/dashboard/career-pilot-simplified-input.integration.test.tsx
new file mode 100644
index 00000000..0c145a87
--- /dev/null
+++ b/apps/applyflow/src/components/dashboard/career-pilot-simplified-input.integration.test.tsx
@@ -0,0 +1,173 @@
+// @vitest-environment jsdom
+import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
+import { render, screen, waitFor, cleanup, within } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import {
+ CareerChatWorkspace,
+ buildSpecialistAnalysisInput,
+} from "./career-chat-workspace";
+import { buildCareerSpecialistFieldsFromSimpleInputs } from "./career-pilot-input-normalizer";
+import { buildPilotCareerBundleFromFields } from "./build-pilot-career-bundle";
+import { EMPTY_CAREER_PILOT_SIMPLE_INPUTS } from "./career-pilot-simple-inputs";
+import * as careerChatClient from "./career-chat-workspace-client";
+
+const resumeText =
+ "Desenvolvi APIs REST em Node.js com TypeScript. Reduzi deploy em 30% com pipelines CI/CD.";
+
+describe("Career pilot simplified input integration", () => {
+ beforeEach(() => {
+ cleanup();
+ vi.restoreAllMocks();
+ });
+
+ afterEach(() => {
+ cleanup();
+ });
+
+ it("preserves resume text when switching from resume to ATS intent", async () => {
+ const user = userEvent.setup();
+ let simpleInputs = { ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS, resumeText };
+
+ const view = render(
+ {
+ simpleInputs = next;
+ }}
+ />,
+ );
+
+ const panel = within(view.getByTestId("career-chat-workspace-panel"));
+ expect((panel.getByTestId("career-pilot-resume-text") as HTMLTextAreaElement).value).toBe(
+ resumeText,
+ );
+
+ await user.click(panel.getByTestId("career-pilot-intent-analyze_ats_compatibility"));
+
+ expect((panel.getByTestId("career-pilot-resume-text") as HTMLTextAreaElement).value).toBe(
+ resumeText,
+ );
+ });
+
+ it("builds compatible analysisInput and CareerBundle from simple inputs", () => {
+ const fields = buildCareerSpecialistFieldsFromSimpleInputs(
+ {
+ ...EMPTY_CAREER_PILOT_SIMPLE_INPUTS,
+ resumeText,
+ jobDescription: "Requisitos:\nExperiência com TypeScript\nInglês intermediário",
+ },
+ "analyze_ats_compatibility",
+ );
+ const bundle = buildPilotCareerBundleFromFields(fields);
+ const analysisInput = buildSpecialistAnalysisInput({
+ action: "analyze_ats_compatibility",
+ fields,
+ mainStack: bundle.candidate?.mainStack ?? [],
+ fallbackRole: bundle.candidate?.targetRole ?? "",
+ });
+
+ expect(bundle.applications.length).toBeGreaterThan(0);
+ expect(analysisInput?.jobSnapshot?.requiredRequirements.length).toBeGreaterThan(0);
+ expect(analysisInput?.jobSnapshot?.keywords).toContain("intermediário");
+ });
+
+ it("does not send resume or job text in feedback payload", async () => {
+ const user = userEvent.setup();
+ const feedbackSpy = vi.spyOn(careerChatClient, "submitCareerFeedback").mockResolvedValue({ ok: true });
+ vi.spyOn(careerChatClient, "runCareerChatLibrechat").mockResolvedValue({
+ status: "completed",
+ provider: "librechat",
+ conversationId: "conv-1",
+ intent: "analyze_resume",
+ agentResult: {
+ status: "completed",
+ agent: "resume_analyst",
+ summary: "ok",
+ findings: [],
+ recommendations: [],
+ evidence: [],
+ warnings: [],
+ reviewRequired: true,
+ safeForClient: true,
+ hasToken: false,
+ rawProviderDataUsed: false,
+ persisted: false,
+ trace: { requestId: "req-1", steps: [] },
+ resumeAnalysis: {
+ score: 70,
+ strengths: [],
+ weaknesses: [],
+ missingEvidence: [],
+ bulletRecommendations: [],
+ sectionRecommendations: [],
+ risks: [],
+ nextActions: [],
+ reviewRequired: true,
+ },
+ },
+ toolProposals: [],
+ warnings: [],
+ reviewRequired: true,
+ safeForClient: true,
+ hasToken: false,
+ persisted: false,
+ executedExternally: false,
+ trace: { conversationId: "conv-1", steps: [] },
+ });
+
+ const view = render(
+ undefined}
+ />,
+ );
+
+ const panel = within(view.getByTestId("career-chat-workspace-panel"));
+ await user.click(panel.getByTestId("career-chat-consent-checkbox"));
+ await user.click(panel.getByTestId("career-chat-send-button"));
+
+ await waitFor(() => {
+ expect(panel.getByTestId("career-pilot-result-view")).toBeTruthy();
+ });
+
+ await user.click(panel.getByTestId("career-pilot-feedback-helpful"));
+ await user.click(panel.getByTestId("career-pilot-feedback-consent"));
+ await user.click(panel.getByTestId("career-pilot-feedback-submit"));
+
+ await waitFor(() => {
+ expect(feedbackSpy).toHaveBeenCalled();
+ });
+
+ const payload = JSON.stringify(feedbackSpy.mock.calls[0]?.[0] ?? {});
+ expect(payload).not.toContain(resumeText);
+ expect(payload).not.toMatch(/resumeText|jobDescription|resumeBullets/i);
+ });
+
+ it("does not use localStorage for pilot inputs", async () => {
+ const setItem = vi.spyOn(Storage.prototype, "setItem");
+ render(
+ undefined}
+ />,
+ );
+
+ expect(setItem).not.toHaveBeenCalled();
+ setItem.mockRestore();
+ });
+});
diff --git a/apps/applyflow/src/components/dashboard/career-polish-classes.ts b/apps/applyflow/src/components/dashboard/career-polish-classes.ts
index 8c5af00f..34a23229 100644
--- a/apps/applyflow/src/components/dashboard/career-polish-classes.ts
+++ b/apps/applyflow/src/components/dashboard/career-polish-classes.ts
@@ -10,6 +10,12 @@ export const careerPolishInput =
export const careerPolishTextarea = `${careerPolishInput} min-h-[5.5rem]`;
+export const careerPolishResumeTextarea =
+ `${careerPolishInput} min-h-[11.25rem] resize-y sm:min-h-[13.75rem]`;
+
+export const careerPolishJobTextarea =
+ `${careerPolishInput} min-h-[11.25rem] resize-y sm:min-h-[13.75rem] w-full`;
+
export const careerPolishSectionSurface =
"rounded-[var(--af-radius)] border border-[color:var(--af-border)] bg-[color:var(--af-surface)]";
diff --git a/docs/career-suite/P01-OPERATIONAL-KIT.md b/docs/career-suite/P01-OPERATIONAL-KIT.md
index 77c33c22..c14c1a24 100644
--- a/docs/career-suite/P01-OPERATIONAL-KIT.md
+++ b/docs/career-suite/P01-OPERATIONAL-KIT.md
@@ -2,7 +2,7 @@
**Related:** [`PILOT-RUNBOOK.md`](./PILOT-RUNBOOK.md) · [`PILOT-VALIDATION.md`](./PILOT-VALIDATION.md) · [`UI-UX-POLISH.md`](./UI-UX-POLISH.md)
-**Operational status (2026-06-22):** `P01 READY TO SCHEDULE` — Preview `main` validated (`7e5dfbc`), preflight técnico e smoke funcional aprovados. Production não promovida.
+**Operational status (2026-06-22):** `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS` — entrada simplificada em implementação na branch `feat/career-suite-simplified-inputs`. Ver [`SIMPLIFIED-INPUT-UX.md`](./SIMPLIFIED-INPUT-UX.md). Production não promovida (`1dfb9de`).
**Preview URL (moderador):** `https://devflow-applyflow-git-main-gustavos-projects-74c68bcc.vercel.app`
@@ -644,10 +644,10 @@ Template: participante, dispositivo, duração, resultado, fluxos, compreensão,
| Fase | Status |
|------|--------|
-| Antes da sessão | `P01 READY TO SCHEDULE` |
+| Antes da sessão | `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS` |
| Após agendamento | `P01 SCHEDULED` |
| Durante | `P01 IN PROGRESS` |
| Após conclusão | `P01 COMPLETED — REVIEW PENDING` |
| Após avaliação | `P01 PASS — CONTINUE TO P02` / `P01 PASS WITH OBSERVATIONS` / `P01 FAILED — FIX BEFORE P02` / `PILOT STOPPED` |
-**Estado atual (2026-06-22):** `P01 READY TO SCHEDULE`
+**Estado atual (2026-06-22):** `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS`
diff --git a/docs/career-suite/PILOT-RUNBOOK.md b/docs/career-suite/PILOT-RUNBOOK.md
index 06871bc3..5a8cefba 100644
--- a/docs/career-suite/PILOT-RUNBOOK.md
+++ b/docs/career-suite/PILOT-RUNBOOK.md
@@ -11,7 +11,7 @@ document complements — and does not replace — [`PILOT-VALIDATION.md`](./PILO
## Product & UX gate (before P01)
-**P01 scheduling resumed** after PR #136 merged and `main` Preview smoke passed (2026-06-22).
+**P01 scheduling paused** during simplified-input implementation (2026-06-22). See [`SIMPLIFIED-INPUT-UX.md`](./SIMPLIFIED-INPUT-UX.md).
| Gate | Source |
|------|--------|
@@ -21,9 +21,9 @@ document complements — and does not replace — [`PILOT-VALIDATION.md`](./PILO
| No open P1 UX blockers | W-01/W-02 resolved; W-04 non-blocking P2 outside pilot surface |
| Technical Preview smoke | [`PILOT-VALIDATION.md`](./PILOT-VALIDATION.md) + `main` smoke 2026-06-22 |
-Current decision (2026-06-22): **`READY FOR P01`** — validated on ApplyFlow Preview `main` @ `7e5dfbc` ([`PRODUCT-UX-READINESS-REVIEW.md`](./PRODUCT-UX-READINESS-REVIEW.md), PR #136 visual revalidation).
+Current decision (2026-06-22): **`SIMPLIFIED INPUT UX IN PROGRESS`** — P01 scheduling paused until Preview visual validation of [`SIMPLIFIED-INPUT-UX.md`](./SIMPLIFIED-INPUT-UX.md) on the feature branch.
-**Operational status:** `P01 READY TO SCHEDULE` — use [`P01-OPERATIONAL-KIT.md`](./P01-OPERATIONAL-KIT.md) for moderator scripts, observation sheets, and preflight checklist.
+**Operational status:** `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS` — use [`P01-OPERATIONAL-KIT.md`](./P01-OPERATIONAL-KIT.md) for moderator scripts; do not schedule P01 until simplified inputs are validated on Preview.
---
diff --git a/docs/career-suite/README.md b/docs/career-suite/README.md
index a1562aeb..9d521283 100644
--- a/docs/career-suite/README.md
+++ b/docs/career-suite/README.md
@@ -98,9 +98,11 @@ diagnostic page. See [`PRODUCTION-READINESS.md`](./PRODUCTION-READINESS.md),
[`DEPLOYMENT.md`](./DEPLOYMENT.md), [`OBSERVABILITY.md`](./OBSERVABILITY.md),
[`PILOT-VALIDATION.md`](./PILOT-VALIDATION.md), [`PILOT-RUNBOOK.md`](./PILOT-RUNBOOK.md),
[`PRODUCT-UX-READINESS-REVIEW.md`](./PRODUCT-UX-READINESS-REVIEW.md),
-[`UI-UX-POLISH.md`](./UI-UX-POLISH.md), and
+[`UI-UX-POLISH.md`](./UI-UX-POLISH.md), [`SIMPLIFIED-INPUT-UX.md`](./SIMPLIFIED-INPUT-UX.md), and
[`SECURITY-CHECKLIST.md`](./SECURITY-CHECKLIST.md).
+**Pilot operational status (2026-06-22):** `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS`.
+
---
## Trust model
@@ -154,6 +156,7 @@ Tests (1,045 across Career Suite packages): [case §12](./CAREER-SUITE-PRODUCT-A
| **P01 operational kit** | [P01-OPERATIONAL-KIT.md](./P01-OPERATIONAL-KIT.md) |
| **Product & UX readiness review** | [PRODUCT-UX-READINESS-REVIEW.md](./PRODUCT-UX-READINESS-REVIEW.md) |
| **UI/UX product polish (pilot)** | [UI-UX-POLISH.md](./UI-UX-POLISH.md) |
+| **Simplified participant inputs (P01 gate)** | [SIMPLIFIED-INPUT-UX.md](./SIMPLIFIED-INPUT-UX.md) |
| Roadmap execution | [ROADMAP-EXECUTION.md](./ROADMAP-EXECUTION.md) |
| Provider integrations | [integrations/README.md](./integrations/README.md) |
| Demo walkthrough | [demo/CAREER-SUITE-WALKTHROUGH.md](./demo/CAREER-SUITE-WALKTHROUGH.md) |
diff --git a/docs/career-suite/SIMPLIFIED-INPUT-UX.md b/docs/career-suite/SIMPLIFIED-INPUT-UX.md
new file mode 100644
index 00000000..800907a5
--- /dev/null
+++ b/docs/career-suite/SIMPLIFIED-INPUT-UX.md
@@ -0,0 +1,76 @@
+# Career Suite Simplified Input UX
+
+## Objective
+
+Allow pilot participants to paste resume and job description as natural text before P01, while keeping existing analysis contracts unchanged.
+
+## User problem
+
+Participants had to structure inputs manually (`Resume bullets`, comma-separated skills, line-separated requirements, target roles). That friction is unrealistic for people who only have a PDF or a full job posting.
+
+## Previous input model
+
+The pilot workspace exposed `CareerSpecialistFields` directly:
+
+- `resumeBullets` (one per line)
+- `resumeSkills` (comma-separated)
+- `jobRequirements` (one per line)
+- `targetRoles` (comma-separated)
+- `availability`
+
+## New input model
+
+Participants use `CareerPilotSimpleInputs`:
+
+| Flow | Fields |
+|------|--------|
+| Analisar currículo | Cargo desejado (opcional), currículo em texto corrido |
+| Comparar com vaga | Currículo, descrição da vaga |
+| Plano de carreira | Objetivo profissional, tempo disponível, preferências/restrições |
+
+## Deterministic normalization
+
+`career-pilot-input-normalizer.ts` maps simple inputs to `CareerSpecialistFields`:
+
+- `normalizeResumeText` / `normalizeJobDescription` — line breaks, empty lines, length limits
+- `extractResumeLines` — bullets from lines or sentences (no invented content)
+- `extractLikelySkills` — small catalog, case-insensitive, deduplicated
+- `extractJobRequirements` — lines or sentences from job text
+- `extractJobKeywords` — Unicode-safe tokenization (`/[^\p{L}\p{N}+#.]+/u`)
+- `buildCareerSpecialistFieldsFromSimpleInputs` — bridge to existing pipeline
+
+No external LLM. No summarization. No invented experiences.
+
+## Optional review
+
+Closed-by-default disclosure **Revisar informações extraídas** lets participants adjust:
+
+- Experiências e resultados
+- Principais competências
+- Requisitos identificados
+- Cargo desejado / tempo disponível
+
+Edits update structured fields in memory only for the current request.
+
+## Privacy
+
+Copy near inputs: *O conteúdo é usado somente nesta análise e não é armazenado automaticamente.*
+
+Data stays in React state for the open page and in the current `/career-chat/librechat` request. No localStorage, cookies, session backend, logs, analytics, or feedback payload.
+
+## Non-goals
+
+- No endpoint, schema, provider, OAuth, or persistence changes
+- No external AI for parsing
+- No Production promotion in this change
+
+## Validation
+
+- ApplyFlow unit/integration tests for normalizer, UI, contracts, privacy
+- Local gates: test, build, lint, design-system, secrets
+
+## Pilot impact
+
+**Operational status:** `P01 SCHEDULING PAUSED — SIMPLIFIED INPUT UX IN PROGRESS`
+
+P01 scheduling remains paused until Preview visual validation (desktop 1440×900, mobile 375×812) is completed on the feature branch PR.