Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/job-readiness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Job Readiness

on:
pull_request:
branches: [main]
push:
branches: [main, "feature/**"]

permissions:
contents: read

jobs:
verify:
name: clean install, typecheck, build
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install from lockfile
run: npm ci

- name: TypeScript check
run: npm run check

- name: Production build
run: npm run build

- name: Verify career surface
shell: bash
run: |
test -f client/src/pages/job-readiness.tsx
test -f docs/JOB_READINESS.md
grep -q 'path="/job-readiness"' client/src/App.tsx
grep -q 'Zyra Job Readiness' client/src/pages/job-readiness.tsx
3 changes: 3 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import TeamPage from "@/pages/team";
import TaskCenterPage from "@/pages/task-center";
import AdminPage from "@/pages/admin";
import EnterpriseReadinessPage from "@/pages/enterprise-readiness";
import JobReadinessPage from "@/pages/job-readiness";
import VerifyEmailPage from "@/pages/verify-email";
import ResetPasswordPage from "@/pages/reset-password";
import AcceptInvitePage from "@/pages/accept-invite";
Expand Down Expand Up @@ -176,6 +177,8 @@ function Router() {
<Route path="/admin">{() => <ProtectedRoute component={AdminPage} />}</Route>
<Route path="/team">{() => <ProtectedRoute component={TeamPage} />}</Route>
<Route path="/enterprise-readiness">{() => <ProtectedRoute component={EnterpriseReadinessPage} />}</Route>
<Route path="/job-readiness">{() => <ProtectedRoute component={JobReadinessPage} />}</Route>
<Route path="/career">{() => <Redirect to="/job-readiness" />}</Route>
Comment on lines +180 to +181

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.

P2 Badge Add the workspace to application navigation

Authenticated users who navigate through the Layout sidebar have no way to discover this route: navGroups contains no Job Readiness entry, and a repository-wide search finds no other link to /job-readiness or /career. Registering the route alone therefore leaves the new first-class workspace accessible only to users who already know and manually enter its URL.

Useful? React with 👍 / 👎.

<Route path="/team-activity">{() => <Redirect to="/team" />}</Route>
<Route path="/oncall-schedule">{() => <Redirect to="/team" />}</Route>
<Route path="/approvals">{() => <Redirect to="/team" />}</Route>
Expand Down
5 changes: 3 additions & 2 deletions client/src/components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Users, ClipboardList, Rocket, Check, X, ListTodo,
GraduationCap, Building, Eye, Map, Fish, Box,
Cpu, GitFork, ScanSearch, Bot, DatabaseZap,
Share2, Database, BarChart2, Layers, Clock, AlertTriangle, ShieldCheck, Shield, Radio
Share2, Database, BarChart2, Layers, Clock, AlertTriangle, ShieldCheck, Shield, Radio, Briefcase
} from "lucide-react";
import zyraLogo from "@assets/ChatGPT_Image_Mar_30,_2026,_05_28_39_PM_1775166956477.png";
import { useTheme } from "./ThemeProvider";
Expand Down Expand Up @@ -44,6 +44,7 @@ const navGroups = [
{ href: "/analytics", label: "Analytics", icon: BarChart3 },
{ href: "/posture", label: "Security Posture", icon: TrendingUp },
{ href: "/onboarding", label: "Getting Started", icon: Rocket },
{ href: "/job-readiness", label: "Job Readiness", icon: Briefcase },
],
},
{
Expand Down Expand Up @@ -500,4 +501,4 @@ export default function Layout({ children }: { children: React.ReactNode }) {
</main>
</div>
);
}
}
258 changes: 258 additions & 0 deletions client/src/pages/job-readiness.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import { Link } from "wouter";
import {
ArrowRight,
BadgeCheck,
Bot,
Briefcase,
CheckCircle2,
Cloud,
Code2,
FileCheck2,
GitBranch,
GraduationCap,
Network,
Rocket,
ShieldCheck,
Sparkles,
Target,
Terminal,
Users,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";

const readinessPillars = [
{
label: "Production engineering",
score: 88,
Comment on lines +27 to +28

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.

P2 Badge Derive readiness scores from verified evidence

Every installation, including a fresh tenant with no portfolio evidence or a currently failing build, receives the same hard-coded scores and an overall value of 86, even though the UI labels this as “Current readiness” and presents it as employer-facing evidence. These values should be calculated from verifiable repository/account state, or clearly presented as non-evaluative guidance rather than readiness measurements.

Useful? React with 👍 / 👎.

icon: Code2,
evidence: "TypeScript, React, Express, API design, PostgreSQL, Drizzle, CI/CD",
},
{
label: "Cloud & DevSecOps",
score: 91,
icon: Cloud,
evidence: "Cloud security, containers, supply chain, CodeQL, deployment workflows",
},
{
label: "AI engineering",
score: 86,
icon: Bot,
evidence: "ZyraCopilot, agent workflows, model integration, evaluation and automation",
},
{
label: "Cybersecurity",
score: 94,
icon: ShieldCheck,
evidence: "Threat intel, incident response, vulnerability management, attack surface",
},
{
label: "Portfolio proof",
score: 82,
icon: GitBranch,
evidence: "Public repository, architecture docs, security controls, deployable product surface",
},
{
label: "Interview readiness",
score: 72,
icon: Users,
evidence: "Translate Zyra architecture into concise system-design and behavioral stories",
},
];

const roleTracks = [
{
title: "AI / Software Engineer",
icon: Sparkles,
fit: "Strong",
focus: ["TypeScript + React", "API architecture", "LLM/agent integration", "Testing + evaluation"],
},
{
title: "Cloud / DevSecOps Engineer",
icon: Network,
fit: "Strong",
focus: ["CI/CD", "Cloud controls", "Containers", "Observability + incident response"],
},
{
title: "Cybersecurity Engineer",
icon: ShieldCheck,
fit: "Strong",
focus: ["Vulnerability management", "Threat intelligence", "Security automation", "Risk + compliance"],
},
{
title: "Platform Engineer",
icon: Terminal,
fit: "Growing",
focus: ["Reliability", "Developer experience", "Deployment", "Operational runbooks"],
},
];

const proofRoutes = [
{ label: "Repositories", href: "/repositories", icon: GitBranch, detail: "Show working source and engineering history." },
{ label: "DevSecOps", href: "/devsecops", icon: Rocket, detail: "Show secure delivery and automation practices." },
{ label: "Cloud Security", href: "/cloud-security", icon: Cloud, detail: "Show cloud control and posture engineering." },
{ label: "Security Copilot", href: "/security-copilot", icon: Bot, detail: "Show applied AI capability inside the platform." },
{ label: "Reports", href: "/reports", icon: FileCheck2, detail: "Show the ability to communicate technical outcomes." },
{ label: "Enterprise Readiness", href: "/enterprise-readiness", icon: BadgeCheck, detail: "Show production and enterprise thinking." },
];

const hiringChecklist = [
"Keep the main branch buildable and type-safe.",
"Pin one production demo and one architecture walkthrough.",
"Document the problem, constraints, architecture, tradeoffs, and measurable result for each flagship feature.",
"Turn security findings and reliability work into STAR-format interview stories.",
"Use issue/PR history as evidence of debugging, review, and engineering iteration.",
"Keep secrets, credentials, customer data, and unverifiable claims out of public portfolio material.",
];

function ScoreBar({ score }: { score: number }) {
return (
<div className="h-2 rounded-full bg-muted overflow-hidden" aria-label={`${score}% readiness`}>
<div className="h-full rounded-full bg-primary" style={{ width: `${score}%` }} />
</div>
);
}

export default function JobReadinessPage() {
const overall = Math.round(readinessPillars.reduce((sum, item) => sum + item.score, 0) / readinessPillars.length);

return (
<div className="space-y-6 max-w-7xl mx-auto" data-testid="job-readiness-page">
<div className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div>
<div className="flex items-center gap-2 mb-2">
<Badge variant="outline" className="gap-1">
<Briefcase className="w-3.5 h-3.5" /> Career Launch
</Badge>
<Badge variant="secondary">Employer-facing</Badge>
</div>
<h1 className="text-3xl font-bold tracking-tight">Zyra Job Readiness</h1>
<p className="text-muted-foreground mt-2 max-w-3xl">
Turn the Zyra ecosystem into verifiable evidence for AI, software, cloud, DevSecOps,
platform, and cybersecurity roles. The goal is not just to claim skills—it is to point to working proof.
</p>
</div>
<div className="rounded-xl border bg-card px-5 py-4 min-w-48">
<div className="text-xs uppercase tracking-wider text-muted-foreground">Current readiness</div>
<div className="flex items-end gap-2 mt-1">
<span className="text-4xl font-bold">{overall}</span>
<span className="text-sm text-muted-foreground mb-1">/ 100</span>
</div>
<ScoreBar score={overall} />
</div>
</div>

<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{readinessPillars.map(({ label, score, icon: Icon, evidence }) => (
<Card key={label}>
<CardHeader className="pb-2">
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<div className="w-9 h-9 rounded-lg bg-primary/10 flex items-center justify-center">
<Icon className="w-4 h-4 text-primary" />
</div>
<CardTitle className="text-base">{label}</CardTitle>
</div>
<span className="text-sm font-semibold">{score}%</span>
</div>
</CardHeader>
<CardContent className="space-y-3">
<ScoreBar score={score} />
<p className="text-xs text-muted-foreground leading-relaxed">{evidence}</p>
</CardContent>
</Card>
))}
</div>

<div className="grid gap-6 xl:grid-cols-[1.1fr_0.9fr]">
<Card>
<CardHeader>
<div className="flex items-center justify-between gap-3">
<div>
<CardTitle className="flex items-center gap-2">
<Target className="w-5 h-5 text-primary" /> Target roles
</CardTitle>
<p className="text-sm text-muted-foreground mt-1">Map the existing platform to real engineering job families.</p>
</div>
<Badge variant="outline">4 tracks</Badge>
</div>
</CardHeader>
<CardContent className="grid gap-3 sm:grid-cols-2">
{roleTracks.map(({ title, icon: Icon, fit, focus }) => (
<div key={title} className="rounded-xl border p-4 bg-muted/20">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 font-semibold text-sm">
<Icon className="w-4 h-4 text-primary" /> {title}
</div>
<Badge variant={fit === "Strong" ? "default" : "secondary"}>{fit}</Badge>
</div>
<div className="mt-3 flex flex-wrap gap-1.5">
{focus.map(item => <Badge key={item} variant="outline" className="font-normal">{item}</Badge>)}
</div>
</div>
))}
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<GraduationCap className="w-5 h-5 text-primary" /> Hiring checklist
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{hiringChecklist.map(item => (
<div key={item} className="flex gap-3 text-sm">
<CheckCircle2 className="w-4 h-4 mt-0.5 shrink-0 text-green-500" />
<span>{item}</span>
</div>
))}
</CardContent>
</Card>
</div>

<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Briefcase className="w-5 h-5 text-primary" /> Portfolio proof inside Zyra
</CardTitle>
<p className="text-sm text-muted-foreground">
Every tile points to an existing product area that can become interview evidence, screenshots, demo material, or a case study.
</p>
</CardHeader>
<CardContent className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
{proofRoutes.map(({ label, href, icon: Icon, detail }) => (
<Link key={href} href={href}>
Comment on lines +225 to +226

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.

P2 Badge Add stable test IDs to the portfolio links

The six proof tiles are interactive navigation elements but lack unique data-testid attributes, so browser tests following the project's selector convention cannot reliably identify or exercise each destination; the final “Review proof” action has the same omission. Add a route- or label-derived test ID to each link/action.

Useful? React with 👍 / 👎.

<div className="group h-full rounded-xl border p-4 hover:border-primary/50 hover:bg-primary/5 transition-colors cursor-pointer">
<div className="flex items-center justify-between">
<Icon className="w-5 h-5 text-primary" />
<ArrowRight className="w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors" />
</div>
<div className="font-semibold mt-3">{label}</div>
<p className="text-xs text-muted-foreground mt-1 leading-relaxed">{detail}</p>
</div>
</Link>
))}
</CardContent>
</Card>

<Card className="border-primary/25 bg-primary/5">
<CardContent className="p-5 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<div className="font-semibold">Definition of job-ready</div>
<p className="text-sm text-muted-foreground mt-1 max-w-3xl">
A recruiter can open the repository, understand what Zyra solves, verify the build and security posture,
see production-minded engineering decisions, and trace your contribution to concrete code and outcomes.
</p>
</div>
<Link href="/repositories">
<Button className="shrink-0">
Review proof <ArrowRight className="w-4 h-4 ml-2" />
</Button>
</Link>
</CardContent>
</Card>
</div>
);
}
51 changes: 51 additions & 0 deletions docs/JOB_READINESS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Zyra Job-Ready Engineering Standard

Zyra should function as both a production-minded security/AI platform and a verifiable engineering portfolio. Job readiness means a recruiter or hiring manager can inspect the repository and quickly understand the problem, architecture, implementation quality, security posture, and engineering tradeoffs.

## Target role families

- AI / software engineering
- Cloud / DevSecOps engineering
- Cybersecurity engineering
- Platform / reliability engineering

## Evidence standard

Every flagship feature should provide evidence across five layers:

1. **Problem** — what real user or operational problem the feature solves.
2. **Architecture** — services, data flows, dependencies, trust boundaries, and deployment assumptions.
3. **Implementation** — readable TypeScript/React/Node code, schemas, APIs, and meaningful commits.
4. **Verification** — type checks, build checks, security scanning, tests where applicable, and documented limitations.
5. **Communication** — screenshots/demo path, concise README or case study, and an interview-ready explanation of tradeoffs and outcomes.

## Job-ready gate

A candidate-facing change is ready when:

- `npm ci` succeeds from a clean checkout.
- `npm run check` succeeds.
- `npm run build` succeeds.
- security workflows remain enabled.
- no credentials, tokens, private customer data, or unverifiable performance claims are committed.
- the feature has a clear route or documented demo path.
- documentation explains what was built and why.

## Interview translation

Use Zyra work to create STAR stories:

- **Situation:** the engineering or security problem.
- **Task:** the outcome or constraint you owned.
- **Action:** architecture, debugging, automation, testing, and tradeoff decisions.
- **Result:** verified build, reduced failure mode, improved workflow, or delivered capability.

Do not inflate metrics. Prefer reproducible proof from code, CI, issues, pull requests, screenshots, and deployed behavior.

## Portfolio safety

Keep public portfolio material free of secrets, credentials, personal data, customer-sensitive information, exploit-ready operational details, and unsupported claims of government or enterprise affiliation.

## Product surface

The in-product `/job-readiness` workspace maps existing Zyra modules to hiring evidence and identifies the next gaps to close before presenting the ecosystem to employers.
Loading