Spaces:
Running
feat: live V1 acceptance — independent audit, 2-parser PDF, summary opt, completeness
Browse filesAdds the pieces needed to prove the real production outcome (no mocks):
- ats_evaluate.py: independent post-generation audit (fresh extraction, scored
from PARSED PDF text) + acceptance_verdict (>=90 only if audit clean)
- pdf_validate.py: verify_keywords_two_parsers (pdftotext + pymupdf/pdfplumber);
every accepted keyword must survive BOTH parsers
- resume_rewrite.py: corpus-verified headline/SUMMARY optimization
(optimize_summary + verify_against_corpus) — reuses only résumé-corpus facts
- llm_client.py: rewrite_summary + extract correction_hint (completeness retry)
- nim_fallback.py: extraction_gaps() completeness check (named tools + years)
- ats_safe.py: wired completeness retry, summary optimization, reportlab ATS-safe
PDF fallback (always produces a parseable PDF), independent audit + 2-parser
verify (run_audit); résumé-source-derived section order for PDF validation
- ats_score.py: parsing gate hinges on recovered-text + no-hidden-markers
- api_server.py: blocking V1 route runs the audit
- scripts/accept_v1_live.py: live 3-case acceptance harness (real JDs in
data/acceptance/); real reportlab PDFs + 2-parser parse + audit
Deterministic suite: 50 passed / 1 skipped. Live run evidence pending.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .gitignore +6 -0
- api_server.py +1 -0
- data/acceptance/jd1_stripe_payments.txt +44 -0
- data/acceptance/jd2_stripe_genai.txt +35 -0
- data/acceptance/jd3_growth_marketing.txt +44 -0
- data/candidate_vault.json +1732 -0
- data/resume/_parsed.json +92 -0
- scripts/accept_v1_live.py +123 -0
- src/ats_evaluate.py +135 -0
- src/ats_safe.py +86 -9
- src/ats_score.py +8 -2
- src/llm_client.py +35 -2
- src/nim_fallback.py +26 -0
- src/pdf_validate.py +63 -2
- src/resume_rewrite.py +85 -0
|
@@ -40,3 +40,9 @@ data/logs/
|
|
| 40 |
|
| 41 |
# Vendor (ever-jobs NestJS monorepo)
|
| 42 |
vendor/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
# Vendor (ever-jobs NestJS monorepo)
|
| 42 |
vendor/
|
| 43 |
+
|
| 44 |
+
# unrelated embedded project + local artifacts
|
| 45 |
+
Job search Git project - Everjobs/
|
| 46 |
+
src/form_autofill.py
|
| 47 |
+
tests/v2_output.pdf
|
| 48 |
+
data/acceptance/out/
|
|
@@ -217,6 +217,7 @@ def latex_flow_for_api(
|
|
| 217 |
company=company or "",
|
| 218 |
job_title=company or job_title or "resume",
|
| 219 |
llm_client=llm, selected_model=sel, model_health=health,
|
|
|
|
| 220 |
out_dir=out_dir,
|
| 221 |
compile_pdf=True,
|
| 222 |
)
|
|
|
|
| 217 |
company=company or "",
|
| 218 |
job_title=company or job_title or "resume",
|
| 219 |
llm_client=llm, selected_model=sel, model_health=health,
|
| 220 |
+
run_audit=True,
|
| 221 |
out_dir=out_dir,
|
| 222 |
compile_pdf=True,
|
| 223 |
)
|
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SOURCE_URL: https://weworkremotely.com/remote-jobs/stripe-staff-product-manager-payments
|
| 2 |
+
|
| 3 |
+
Who we are
|
| 4 |
+
About Stripe
|
| 5 |
+
|
| 6 |
+
Stripe is a financial infrastructure platform for businesses. Millions of companies - from the world's largest enterprises to the most ambitious startups - use Stripe to accept payments, grow their revenue, and accelerate new business opportunities. Our mission is to increase the GDP of the internet, and we have a staggering amount of work ahead. That means you have an unprecedented opportunity to put the global economy within everyone's reach while doing the most important work of your career.
|
| 7 |
+
|
| 8 |
+
About the Organization
|
| 9 |
+
|
| 10 |
+
The Payments organization focuses on developing products and platforms that enable users to accept payments from customers efficiently. This includes building APIs for processing payments, enabling regional, non-card payment options, and extending Stripe's capabilities to make it easy for businesses to accept in-person payments. Optimized Checkout and Link teams work to create best-in-class checkout experiences that enhance customer satisfaction and drive merchant conversion rates.
|
| 11 |
+
|
| 12 |
+
Team Matching: exact team matching for one of the subteams within this org will begin during final stages. Please note we may also consider you for different orgs based on your experience, location, etc. More information on our team matching process can be found here.
|
| 13 |
+
|
| 14 |
+
Regardless of team, you will own a critical piece of Stripe's payments stack, define product strategy that balances user needs with infrastructure complexity, and ship products that directly impact Stripe's revenue and the success of millions of businesses.
|
| 15 |
+
|
| 16 |
+
What you'll do
|
| 17 |
+
|
| 18 |
+
We're looking for Staff Product Managers who deeply understand their customers and want to make an impact on managing money at a global scale. Our team collaborates with many cross-functional teams at Stripe to deliver innovative solutions that address evolving user needs.
|
| 19 |
+
|
| 20 |
+
Responsibilities
|
| 21 |
+
Develop the long-term vision and strategy for your pillar and create and execute on a compelling roadmap
|
| 22 |
+
Build deep user empathy with both Stripe's merchants and financial partners, understanding global payment industry trends and competitors' offerings to influence Stripe's roadmap effectively
|
| 23 |
+
Collaborate with engineering, design, legal, and other teams to jointly shape product experiences that delight Stripe's users
|
| 24 |
+
Ensure we are building a reliable, performant platform, including system reliability, API extensibility, and latency
|
| 25 |
+
Use metrics to inform your point of view and leverage analytics to measure success
|
| 26 |
+
|
| 27 |
+
Who you are
|
| 28 |
+
|
| 29 |
+
We're looking for someone who meets the minimum requirements to be considered for the role. If you meet these requirements, you are encouraged to apply. The preferred qualifications are a bonus, not a requirement.
|
| 30 |
+
|
| 31 |
+
Minimum requirements
|
| 32 |
+
10+ years of industry product management experience (does not include internships nor includes co-ops)
|
| 33 |
+
Experience in payments, fintech, financial infrastructure, or a closely adjacent domain (e.g., commerce platforms, banking, payment networks) and a track record of building highly impactful products
|
| 34 |
+
Demonstrated experience partnering closely with engineers, designers, and external partners to build products and complex systems at scale
|
| 35 |
+
Strong written and verbal communication skills, with a knack for precise and concise articulation of user problems
|
| 36 |
+
Strong analytical capabilities with experience defining and tracking success metrics, running experiments (e.g., holdback tests), and using data to measure product impact on revenue, conversion, and margins
|
| 37 |
+
Experience working on products that require aligning multiple stakeholders, developing joint roadmaps, and navigating competing priorities between internal goals and external partner objectives
|
| 38 |
+
Ability to thrive in a dynamic and fast-paced environment with significant autonomy and responsibility
|
| 39 |
+
Background delivering successfully on multi-quarter roadmaps and aligning and influencing diverse stakeholders to achieve shared goals
|
| 40 |
+
Strong business acumen and comfort with complex ecosystem and platform-level problems
|
| 41 |
+
Preferred qualifications
|
| 42 |
+
Bachelor's degree (or relevant degree equivalent): Computer Science, Engineering, Information Systems, Analytics, Mathematics, Physics, Applied Sciences
|
| 43 |
+
Demonstrated success and strong execution track record in building payment products
|
| 44 |
+
Experience working at scale - you've managed products handling high transaction volumes where availability, latency, and marginal basis points matter
|
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SOURCE_URL: https://weworkremotely.com/remote-jobs/stripe-staff-product-manager-ml-foundations-and-genai
|
| 2 |
+
|
| 3 |
+
Who we are
|
| 4 |
+
About Stripe
|
| 5 |
+
|
| 6 |
+
Stripe is a financial infrastructure platform for businesses. Millions of companies—from the world's largest enterprises to the most ambitious startups—use Stripe to accept payments, grow their revenue, and accelerate new business opportunities. Our mission is to increase the GDP of the internet, and we have a staggering amount of work ahead. That means you have an unprecedented opportunity to put the global economy within everyone's reach while doing the most important work of your career.
|
| 7 |
+
|
| 8 |
+
About the team
|
| 9 |
+
|
| 10 |
+
You will be joining Stripe's ML Foundations and Gen AI team to incubate new ML applications and improve our ML capabilities across Stripe. Our team is responsible for unlocking novel ML and LLM techniques and applications across Stripe's product suite to drive business outcomes, as well as providing infrastructure, tooling and support for ML teams.
|
| 11 |
+
|
| 12 |
+
What you'll do
|
| 13 |
+
|
| 14 |
+
As a senior product leader, you will lead a cross-functional team to define, incubate and scale new ML/AI applications across Stripe's product suite, and drive our strategy and roadmap for ML/AI infrastructure powering all of Stripe's teams. You will work closely with product leaders across business units to define and deliver on an AI-centric product strategy, launching new applications that drive incremental business outcomes. At the same time, you will be advancing our core AI technology stack to empower teams across Stripe to infuse their scenarios with Agents and agentic capabilities, with API support for agent quality and continuous improvement.
|
| 15 |
+
|
| 16 |
+
Responsibilities
|
| 17 |
+
Develop and execute on the Stripe-wide strategy for new ML/AI applications across our product suite
|
| 18 |
+
Evaluate and align on areas of investment for ML/AI applications in collaboration with product leaders across the company
|
| 19 |
+
Work with cross-functional teams to execute on the roadmap and launch successful new ML/AI applications
|
| 20 |
+
Communicate clearly and crisply with leadership stakeholders and drive alignment across multiple teams
|
| 21 |
+
Develop and execute on a strategy for advancing Stripe's ML/AI infrastructure and tooling
|
| 22 |
+
Who you are
|
| 23 |
+
|
| 24 |
+
We're looking for someone who meets the requirements below, and has a passion for AI to be considered for the role. If you meet these requirements, you are encouraged to apply. The preferred qualifications are a bonus, not a requirement.
|
| 25 |
+
|
| 26 |
+
Minimum requirements
|
| 27 |
+
7+ years of experience delivering highly successful and innovative software products which are ML powered
|
| 28 |
+
Solid understanding of ML and applied AI tech stacks
|
| 29 |
+
Demonstrated ability to influence company level strategy and work with business leaders to execute on the transformation
|
| 30 |
+
You push the pace. You take blame and pass the praise. People love working with you.
|
| 31 |
+
Proven ability to lead teams and work cross-functionally in a highly collaborative environment.
|
| 32 |
+
Ability to analyze and use quantitative and qualitative data to inform decisions.
|
| 33 |
+
A deep understanding and empathy for consumer and business users — you love building products that make our customers feel joy, delight and trust.
|
| 34 |
+
Relentlessly drives product quality
|
| 35 |
+
Capable of working on both 1P and 3P products
|
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
SOURCE_URL: https://weworkremotely.com/remote-jobs/power-digital-growth-marketing-manager-b2b-saas
|
| 2 |
+
|
| 3 |
+
Who We Are:
|
| 4 |
+
|
| 5 |
+
We are a tech-enabled growth firm–at the intersection of marketing, consulting & data intelligence–igniting revenue and brand recognition for leading and emerging companies around the world. As a people-first firm, we value diversity in backgrounds and experiences. We strongly believe our people and culture are key to our success.
|
| 6 |
+
|
| 7 |
+
As a full-service growth marketing firm, we offer best-in-class services including: SEO, Content Marketing, Paid Media, Social Media Marketing, Programmatic + CTV, Public Relations, Influencer Marketing, Email + SMS, Conversion Rate Optimization, Retail Marketing, and Creative.
|
| 8 |
+
|
| 9 |
+
At the heart of Power Digital is our proprietary technology, nova, which analyzes businesses through first-party data, simplifying investment planning for marketing and diligence in M&A.
|
| 10 |
+
|
| 11 |
+
A day in the life:
|
| 12 |
+
|
| 13 |
+
As a Growth Marketing Manager, you'll own the strategy and execution behind the integrated marketing campaigns that fuel Cardinal's pipeline. You'll architect full-funnel initiatives, from webinars and events to reports and eBooks, then lead cross-functional teams to bring each campaign to life with cohesive messaging and high-impact creative. Daily, you'll monitor performance, optimize in real time, and ensure that every channel works together to generate high-quality leads. You'll collaborate closely with internal contributors and contractors, keeping projects on track and jumping in to execute when needed.
|
| 14 |
+
|
| 15 |
+
Responsibilities:
|
| 16 |
+
|
| 17 |
+
Campaign Strategy & Planning
|
| 18 |
+
Build full-funnel campaign strategies for major launches (webinars, in-person events, eBooks, reports).
|
| 19 |
+
Define audiences, messaging, channel mix, goals, and success metrics.
|
| 20 |
+
Develop campaign plans and timelines aligned with growth and revenue priorities.
|
| 21 |
+
Integrated Activation
|
| 22 |
+
Lead cross-channel execution across email, paid media, social, and web.
|
| 23 |
+
Create creative briefs and coordinate with design, copy, video, paid, and email teams.
|
| 24 |
+
Ensure campaigns land with cohesive messaging, strong creative, and clear CTAs.
|
| 25 |
+
Performance & Optimization
|
| 26 |
+
Monitor performance and identify what's working, what's not, and why.
|
| 27 |
+
Optimize live campaigns—messaging, audience, UX, offers, funnel flow.
|
| 28 |
+
Build testing plans and share insights to improve results continually.
|
| 29 |
+
Collaboration & Leadership
|
| 30 |
+
Manage internal contributors and contractors, ensuring deadlines and quality.
|
| 31 |
+
Step in to execute when needed—no ego, high ownership.
|
| 32 |
+
|
| 33 |
+
Role Requirements:
|
| 34 |
+
|
| 35 |
+
Data-driven, audience-obsessed growth marketer.
|
| 36 |
+
Comfortable owning both strategy and execution.
|
| 37 |
+
Strong communicator across creative and performance teams.
|
| 38 |
+
5–7 years in B2B growth, demand gen, or integrated marketing (agency/professional services a plus).
|
| 39 |
+
Demonstrated success running multi-channel, full-funnel campaigns with measurable results.
|
| 40 |
+
Expertise in LinkedIn Ads, Meta Ads, Google Ads, email marketing platforms, and webinar tools.
|
| 41 |
+
Deep understanding of digital channels and how they work together.
|
| 42 |
+
Ability to write or refine campaign copy (emails, landing pages, ads).
|
| 43 |
+
Strong analytical skills: reporting, performance diagnosis, and conversion optimization.
|
| 44 |
+
Experience thriving in fast-paced environments with high standards.
|
|
@@ -0,0 +1,1732 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"entries": [
|
| 3 |
+
{
|
| 4 |
+
"term": "product management",
|
| 5 |
+
"category": "hard_skill",
|
| 6 |
+
"source": "resume_original",
|
| 7 |
+
"confidence": "high",
|
| 8 |
+
"usage_guidance": "safe_to_use",
|
| 9 |
+
"example_bullet": ""
|
| 10 |
+
},
|
| 11 |
+
{
|
| 12 |
+
"term": "agile/scrum",
|
| 13 |
+
"category": "hard_skill",
|
| 14 |
+
"source": "inferred_plausible",
|
| 15 |
+
"confidence": "medium",
|
| 16 |
+
"usage_guidance": "use_carefully",
|
| 17 |
+
"example_bullet": ""
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"term": "feedback",
|
| 21 |
+
"category": "hard_skill",
|
| 22 |
+
"source": "inferred_plausible",
|
| 23 |
+
"confidence": "medium",
|
| 24 |
+
"usage_guidance": "use_carefully",
|
| 25 |
+
"example_bullet": ""
|
| 26 |
+
},
|
| 27 |
+
{
|
| 28 |
+
"term": "user",
|
| 29 |
+
"category": "hard_skill",
|
| 30 |
+
"source": "resume_original",
|
| 31 |
+
"confidence": "high",
|
| 32 |
+
"usage_guidance": "safe_to_use",
|
| 33 |
+
"example_bullet": ""
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"term": "competitor analysis",
|
| 37 |
+
"category": "hard_skill",
|
| 38 |
+
"source": "inferred_plausible",
|
| 39 |
+
"confidence": "medium",
|
| 40 |
+
"usage_guidance": "use_carefully",
|
| 41 |
+
"example_bullet": ""
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"term": "product lifecycle",
|
| 45 |
+
"category": "hard_skill",
|
| 46 |
+
"source": "resume_original",
|
| 47 |
+
"confidence": "high",
|
| 48 |
+
"usage_guidance": "safe_to_use",
|
| 49 |
+
"example_bullet": ""
|
| 50 |
+
},
|
| 51 |
+
{
|
| 52 |
+
"term": "product manager",
|
| 53 |
+
"category": "hard_skill",
|
| 54 |
+
"source": "resume_original",
|
| 55 |
+
"confidence": "high",
|
| 56 |
+
"usage_guidance": "safe_to_use",
|
| 57 |
+
"example_bullet": ""
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"term": "problem-solving",
|
| 61 |
+
"category": "hard_skill",
|
| 62 |
+
"source": "inferred_plausible",
|
| 63 |
+
"confidence": "medium",
|
| 64 |
+
"usage_guidance": "use_carefully",
|
| 65 |
+
"example_bullet": ""
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"term": "product vision",
|
| 69 |
+
"category": "hard_skill",
|
| 70 |
+
"source": "resume_original",
|
| 71 |
+
"confidence": "high",
|
| 72 |
+
"usage_guidance": "safe_to_use",
|
| 73 |
+
"example_bullet": ""
|
| 74 |
+
},
|
| 75 |
+
{
|
| 76 |
+
"term": "data-driven",
|
| 77 |
+
"category": "hard_skill",
|
| 78 |
+
"source": "resume_original",
|
| 79 |
+
"confidence": "high",
|
| 80 |
+
"usage_guidance": "safe_to_use",
|
| 81 |
+
"example_bullet": ""
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"term": "roadmap",
|
| 85 |
+
"category": "hard_skill",
|
| 86 |
+
"source": "resume_original",
|
| 87 |
+
"confidence": "high",
|
| 88 |
+
"usage_guidance": "safe_to_use",
|
| 89 |
+
"example_bullet": ""
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"term": "kpis",
|
| 93 |
+
"category": "hard_skill",
|
| 94 |
+
"source": "resume_original",
|
| 95 |
+
"confidence": "high",
|
| 96 |
+
"usage_guidance": "safe_to_use",
|
| 97 |
+
"example_bullet": ""
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"term": "integrations",
|
| 101 |
+
"category": "hard_skill",
|
| 102 |
+
"source": "resume_original",
|
| 103 |
+
"confidence": "high",
|
| 104 |
+
"usage_guidance": "safe_to_use",
|
| 105 |
+
"example_bullet": ""
|
| 106 |
+
},
|
| 107 |
+
{
|
| 108 |
+
"term": "metrics",
|
| 109 |
+
"category": "hard_skill",
|
| 110 |
+
"source": "inferred_plausible",
|
| 111 |
+
"confidence": "medium",
|
| 112 |
+
"usage_guidance": "use_carefully",
|
| 113 |
+
"example_bullet": ""
|
| 114 |
+
},
|
| 115 |
+
{
|
| 116 |
+
"term": "iteration",
|
| 117 |
+
"category": "hard_skill",
|
| 118 |
+
"source": "inferred_plausible",
|
| 119 |
+
"confidence": "medium",
|
| 120 |
+
"usage_guidance": "use_carefully",
|
| 121 |
+
"example_bullet": ""
|
| 122 |
+
},
|
| 123 |
+
{
|
| 124 |
+
"term": "customers",
|
| 125 |
+
"category": "hard_skill",
|
| 126 |
+
"source": "resume_original",
|
| 127 |
+
"confidence": "high",
|
| 128 |
+
"usage_guidance": "safe_to_use",
|
| 129 |
+
"example_bullet": ""
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"term": "backend",
|
| 133 |
+
"category": "hard_skill",
|
| 134 |
+
"source": "inferred_plausible",
|
| 135 |
+
"confidence": "medium",
|
| 136 |
+
"usage_guidance": "use_carefully",
|
| 137 |
+
"example_bullet": ""
|
| 138 |
+
},
|
| 139 |
+
{
|
| 140 |
+
"term": "end-to-end",
|
| 141 |
+
"category": "hard_skill",
|
| 142 |
+
"source": "resume_original",
|
| 143 |
+
"confidence": "high",
|
| 144 |
+
"usage_guidance": "safe_to_use",
|
| 145 |
+
"example_bullet": ""
|
| 146 |
+
},
|
| 147 |
+
{
|
| 148 |
+
"term": "environment",
|
| 149 |
+
"category": "hard_skill",
|
| 150 |
+
"source": "inferred_plausible",
|
| 151 |
+
"confidence": "medium",
|
| 152 |
+
"usage_guidance": "use_carefully",
|
| 153 |
+
"example_bullet": ""
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"term": "systems",
|
| 157 |
+
"category": "hard_skill",
|
| 158 |
+
"source": "resume_original",
|
| 159 |
+
"confidence": "high",
|
| 160 |
+
"usage_guidance": "safe_to_use",
|
| 161 |
+
"example_bullet": ""
|
| 162 |
+
},
|
| 163 |
+
{
|
| 164 |
+
"term": "design",
|
| 165 |
+
"category": "hard_skill",
|
| 166 |
+
"source": "resume_original",
|
| 167 |
+
"confidence": "high",
|
| 168 |
+
"usage_guidance": "safe_to_use",
|
| 169 |
+
"example_bullet": ""
|
| 170 |
+
},
|
| 171 |
+
{
|
| 172 |
+
"term": "strategy",
|
| 173 |
+
"category": "hard_skill",
|
| 174 |
+
"source": "resume_original",
|
| 175 |
+
"confidence": "high",
|
| 176 |
+
"usage_guidance": "safe_to_use",
|
| 177 |
+
"example_bullet": ""
|
| 178 |
+
},
|
| 179 |
+
{
|
| 180 |
+
"term": "alignment",
|
| 181 |
+
"category": "hard_skill",
|
| 182 |
+
"source": "inferred_plausible",
|
| 183 |
+
"confidence": "medium",
|
| 184 |
+
"usage_guidance": "use_carefully",
|
| 185 |
+
"example_bullet": ""
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"term": "customer needs",
|
| 189 |
+
"category": "hard_skill",
|
| 190 |
+
"source": "inferred_plausible",
|
| 191 |
+
"confidence": "medium",
|
| 192 |
+
"usage_guidance": "use_carefully",
|
| 193 |
+
"example_bullet": ""
|
| 194 |
+
},
|
| 195 |
+
{
|
| 196 |
+
"term": "development",
|
| 197 |
+
"category": "hard_skill",
|
| 198 |
+
"source": "resume_original",
|
| 199 |
+
"confidence": "high",
|
| 200 |
+
"usage_guidance": "safe_to_use",
|
| 201 |
+
"example_bullet": ""
|
| 202 |
+
},
|
| 203 |
+
{
|
| 204 |
+
"term": "launch",
|
| 205 |
+
"category": "hard_skill",
|
| 206 |
+
"source": "resume_original",
|
| 207 |
+
"confidence": "high",
|
| 208 |
+
"usage_guidance": "safe_to_use",
|
| 209 |
+
"example_bullet": ""
|
| 210 |
+
},
|
| 211 |
+
{
|
| 212 |
+
"term": "product requirements",
|
| 213 |
+
"category": "hard_skill",
|
| 214 |
+
"source": "inferred_plausible",
|
| 215 |
+
"confidence": "medium",
|
| 216 |
+
"usage_guidance": "use_carefully",
|
| 217 |
+
"example_bullet": ""
|
| 218 |
+
},
|
| 219 |
+
{
|
| 220 |
+
"term": "quality",
|
| 221 |
+
"category": "hard_skill",
|
| 222 |
+
"source": "inferred_plausible",
|
| 223 |
+
"confidence": "medium",
|
| 224 |
+
"usage_guidance": "use_carefully",
|
| 225 |
+
"example_bullet": ""
|
| 226 |
+
},
|
| 227 |
+
{
|
| 228 |
+
"term": "engineering",
|
| 229 |
+
"category": "hard_skill",
|
| 230 |
+
"source": "resume_original",
|
| 231 |
+
"confidence": "high",
|
| 232 |
+
"usage_guidance": "safe_to_use",
|
| 233 |
+
"example_bullet": ""
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"term": "teams",
|
| 237 |
+
"category": "hard_skill",
|
| 238 |
+
"source": "resume_original",
|
| 239 |
+
"confidence": "high",
|
| 240 |
+
"usage_guidance": "safe_to_use",
|
| 241 |
+
"example_bullet": ""
|
| 242 |
+
},
|
| 243 |
+
{
|
| 244 |
+
"term": "business",
|
| 245 |
+
"category": "hard_skill",
|
| 246 |
+
"source": "resume_original",
|
| 247 |
+
"confidence": "high",
|
| 248 |
+
"usage_guidance": "safe_to_use",
|
| 249 |
+
"example_bullet": ""
|
| 250 |
+
},
|
| 251 |
+
{
|
| 252 |
+
"term": "stakeholders",
|
| 253 |
+
"category": "hard_skill",
|
| 254 |
+
"source": "resume_original",
|
| 255 |
+
"confidence": "high",
|
| 256 |
+
"usage_guidance": "safe_to_use",
|
| 257 |
+
"example_bullet": ""
|
| 258 |
+
},
|
| 259 |
+
{
|
| 260 |
+
"term": "priorities",
|
| 261 |
+
"category": "hard_skill",
|
| 262 |
+
"source": "inferred_plausible",
|
| 263 |
+
"confidence": "medium",
|
| 264 |
+
"usage_guidance": "use_carefully",
|
| 265 |
+
"example_bullet": ""
|
| 266 |
+
},
|
| 267 |
+
{
|
| 268 |
+
"term": "performance",
|
| 269 |
+
"category": "hard_skill",
|
| 270 |
+
"source": "resume_original",
|
| 271 |
+
"confidence": "high",
|
| 272 |
+
"usage_guidance": "safe_to_use",
|
| 273 |
+
"example_bullet": ""
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
"term": "data",
|
| 277 |
+
"category": "hard_skill",
|
| 278 |
+
"source": "resume_original",
|
| 279 |
+
"confidence": "high",
|
| 280 |
+
"usage_guidance": "safe_to_use",
|
| 281 |
+
"example_bullet": ""
|
| 282 |
+
},
|
| 283 |
+
{
|
| 284 |
+
"term": "products",
|
| 285 |
+
"category": "hard_skill",
|
| 286 |
+
"source": "resume_original",
|
| 287 |
+
"confidence": "high",
|
| 288 |
+
"usage_guidance": "safe_to_use",
|
| 289 |
+
"example_bullet": ""
|
| 290 |
+
},
|
| 291 |
+
{
|
| 292 |
+
"term": "delivery",
|
| 293 |
+
"category": "hard_skill",
|
| 294 |
+
"source": "resume_original",
|
| 295 |
+
"confidence": "high",
|
| 296 |
+
"usage_guidance": "safe_to_use",
|
| 297 |
+
"example_bullet": ""
|
| 298 |
+
},
|
| 299 |
+
{
|
| 300 |
+
"term": "communicate",
|
| 301 |
+
"category": "hard_skill",
|
| 302 |
+
"source": "inferred_plausible",
|
| 303 |
+
"confidence": "medium",
|
| 304 |
+
"usage_guidance": "use_carefully",
|
| 305 |
+
"example_bullet": ""
|
| 306 |
+
},
|
| 307 |
+
{
|
| 308 |
+
"term": "decision-making",
|
| 309 |
+
"category": "hard_skill",
|
| 310 |
+
"source": "resume_original",
|
| 311 |
+
"confidence": "high",
|
| 312 |
+
"usage_guidance": "safe_to_use",
|
| 313 |
+
"example_bullet": ""
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"term": "confluence",
|
| 317 |
+
"category": "tool",
|
| 318 |
+
"source": "inferred_plausible",
|
| 319 |
+
"confidence": "medium",
|
| 320 |
+
"usage_guidance": "use_carefully",
|
| 321 |
+
"example_bullet": ""
|
| 322 |
+
},
|
| 323 |
+
{
|
| 324 |
+
"term": "jira",
|
| 325 |
+
"category": "tool",
|
| 326 |
+
"source": "inferred_plausible",
|
| 327 |
+
"confidence": "medium",
|
| 328 |
+
"usage_guidance": "use_carefully",
|
| 329 |
+
"example_bullet": ""
|
| 330 |
+
},
|
| 331 |
+
{
|
| 332 |
+
"term": "market research",
|
| 333 |
+
"category": "responsibility",
|
| 334 |
+
"source": "resume_original",
|
| 335 |
+
"confidence": "high",
|
| 336 |
+
"usage_guidance": "safe_to_use",
|
| 337 |
+
"example_bullet": ""
|
| 338 |
+
},
|
| 339 |
+
{
|
| 340 |
+
"term": "edtech",
|
| 341 |
+
"category": "domain",
|
| 342 |
+
"source": "resume_original",
|
| 343 |
+
"confidence": "high",
|
| 344 |
+
"usage_guidance": "safe_to_use",
|
| 345 |
+
"example_bullet": ""
|
| 346 |
+
},
|
| 347 |
+
{
|
| 348 |
+
"term": "fintech",
|
| 349 |
+
"category": "domain",
|
| 350 |
+
"source": "inferred_plausible",
|
| 351 |
+
"confidence": "medium",
|
| 352 |
+
"usage_guidance": "use_carefully",
|
| 353 |
+
"example_bullet": ""
|
| 354 |
+
},
|
| 355 |
+
{
|
| 356 |
+
"term": "saas",
|
| 357 |
+
"category": "domain",
|
| 358 |
+
"source": "inferred_plausible",
|
| 359 |
+
"confidence": "medium",
|
| 360 |
+
"usage_guidance": "use_carefully",
|
| 361 |
+
"example_bullet": ""
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
"term": "degree in",
|
| 365 |
+
"category": "education",
|
| 366 |
+
"source": "inferred_plausible",
|
| 367 |
+
"confidence": "medium",
|
| 368 |
+
"usage_guidance": "use_carefully",
|
| 369 |
+
"example_bullet": ""
|
| 370 |
+
},
|
| 371 |
+
{
|
| 372 |
+
"term": "bachelor's",
|
| 373 |
+
"category": "education",
|
| 374 |
+
"source": "inferred_plausible",
|
| 375 |
+
"confidence": "medium",
|
| 376 |
+
"usage_guidance": "use_carefully",
|
| 377 |
+
"example_bullet": ""
|
| 378 |
+
},
|
| 379 |
+
{
|
| 380 |
+
"term": "stakeholder management",
|
| 381 |
+
"category": "soft_skill",
|
| 382 |
+
"source": "resume_original",
|
| 383 |
+
"confidence": "high",
|
| 384 |
+
"usage_guidance": "safe_to_use",
|
| 385 |
+
"example_bullet": ""
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"term": "strategic thinking",
|
| 389 |
+
"category": "soft_skill",
|
| 390 |
+
"source": "inferred_plausible",
|
| 391 |
+
"confidence": "medium",
|
| 392 |
+
"usage_guidance": "use_carefully",
|
| 393 |
+
"example_bullet": ""
|
| 394 |
+
},
|
| 395 |
+
{
|
| 396 |
+
"term": "cross-functional",
|
| 397 |
+
"category": "soft_skill",
|
| 398 |
+
"source": "resume_original",
|
| 399 |
+
"confidence": "high",
|
| 400 |
+
"usage_guidance": "safe_to_use",
|
| 401 |
+
"example_bullet": ""
|
| 402 |
+
},
|
| 403 |
+
{
|
| 404 |
+
"term": "communication",
|
| 405 |
+
"category": "soft_skill",
|
| 406 |
+
"source": "resume_original",
|
| 407 |
+
"confidence": "high",
|
| 408 |
+
"usage_guidance": "safe_to_use",
|
| 409 |
+
"example_bullet": ""
|
| 410 |
+
},
|
| 411 |
+
{
|
| 412 |
+
"term": "leadership",
|
| 413 |
+
"category": "soft_skill",
|
| 414 |
+
"source": "resume_original",
|
| 415 |
+
"confidence": "high",
|
| 416 |
+
"usage_guidance": "safe_to_use",
|
| 417 |
+
"example_bullet": ""
|
| 418 |
+
},
|
| 419 |
+
{
|
| 420 |
+
"term": "collaboration",
|
| 421 |
+
"category": "soft_skill",
|
| 422 |
+
"source": "resume_original",
|
| 423 |
+
"confidence": "high",
|
| 424 |
+
"usage_guidance": "safe_to_use",
|
| 425 |
+
"example_bullet": ""
|
| 426 |
+
},
|
| 427 |
+
{
|
| 428 |
+
"term": "lead",
|
| 429 |
+
"category": "seniority",
|
| 430 |
+
"source": "resume_original",
|
| 431 |
+
"confidence": "high",
|
| 432 |
+
"usage_guidance": "safe_to_use",
|
| 433 |
+
"example_bullet": ""
|
| 434 |
+
},
|
| 435 |
+
{
|
| 436 |
+
"term": "7 years",
|
| 437 |
+
"category": "seniority",
|
| 438 |
+
"source": "blocked",
|
| 439 |
+
"confidence": "low",
|
| 440 |
+
"usage_guidance": "blocked",
|
| 441 |
+
"example_bullet": ""
|
| 442 |
+
},
|
| 443 |
+
{
|
| 444 |
+
"term": "threat intelligence",
|
| 445 |
+
"category": "hard_skill",
|
| 446 |
+
"source": "risky_review",
|
| 447 |
+
"confidence": "low",
|
| 448 |
+
"usage_guidance": "ask_user",
|
| 449 |
+
"example_bullet": ""
|
| 450 |
+
},
|
| 451 |
+
{
|
| 452 |
+
"term": "technologies",
|
| 453 |
+
"category": "hard_skill",
|
| 454 |
+
"source": "resume_original",
|
| 455 |
+
"confidence": "high",
|
| 456 |
+
"usage_guidance": "safe_to_use",
|
| 457 |
+
"example_bullet": ""
|
| 458 |
+
},
|
| 459 |
+
{
|
| 460 |
+
"term": "security operations",
|
| 461 |
+
"category": "hard_skill",
|
| 462 |
+
"source": "risky_review",
|
| 463 |
+
"confidence": "low",
|
| 464 |
+
"usage_guidance": "ask_user",
|
| 465 |
+
"example_bullet": ""
|
| 466 |
+
},
|
| 467 |
+
{
|
| 468 |
+
"term": "product development",
|
| 469 |
+
"category": "hard_skill",
|
| 470 |
+
"source": "resume_original",
|
| 471 |
+
"confidence": "high",
|
| 472 |
+
"usage_guidance": "safe_to_use",
|
| 473 |
+
"example_bullet": ""
|
| 474 |
+
},
|
| 475 |
+
{
|
| 476 |
+
"term": "threat detection",
|
| 477 |
+
"category": "hard_skill",
|
| 478 |
+
"source": "risky_review",
|
| 479 |
+
"confidence": "low",
|
| 480 |
+
"usage_guidance": "ask_user",
|
| 481 |
+
"example_bullet": ""
|
| 482 |
+
},
|
| 483 |
+
{
|
| 484 |
+
"term": "user experience",
|
| 485 |
+
"category": "hard_skill",
|
| 486 |
+
"source": "resume_original",
|
| 487 |
+
"confidence": "high",
|
| 488 |
+
"usage_guidance": "safe_to_use",
|
| 489 |
+
"example_bullet": ""
|
| 490 |
+
},
|
| 491 |
+
{
|
| 492 |
+
"term": "data analysis",
|
| 493 |
+
"category": "hard_skill",
|
| 494 |
+
"source": "inferred_plausible",
|
| 495 |
+
"confidence": "medium",
|
| 496 |
+
"usage_guidance": "use_carefully",
|
| 497 |
+
"example_bullet": ""
|
| 498 |
+
},
|
| 499 |
+
{
|
| 500 |
+
"term": "go-to-market",
|
| 501 |
+
"category": "hard_skill",
|
| 502 |
+
"source": "resume_original",
|
| 503 |
+
"confidence": "high",
|
| 504 |
+
"usage_guidance": "safe_to_use",
|
| 505 |
+
"example_bullet": ""
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"term": "analytics",
|
| 509 |
+
"category": "hard_skill",
|
| 510 |
+
"source": "inferred_plausible",
|
| 511 |
+
"confidence": "medium",
|
| 512 |
+
"usage_guidance": "use_carefully",
|
| 513 |
+
"example_bullet": ""
|
| 514 |
+
},
|
| 515 |
+
{
|
| 516 |
+
"term": "prioritization",
|
| 517 |
+
"category": "hard_skill",
|
| 518 |
+
"source": "resume_original",
|
| 519 |
+
"confidence": "high",
|
| 520 |
+
"usage_guidance": "safe_to_use",
|
| 521 |
+
"example_bullet": ""
|
| 522 |
+
},
|
| 523 |
+
{
|
| 524 |
+
"term": "cloud",
|
| 525 |
+
"category": "hard_skill",
|
| 526 |
+
"source": "inferred_plausible",
|
| 527 |
+
"confidence": "medium",
|
| 528 |
+
"usage_guidance": "use_carefully",
|
| 529 |
+
"example_bullet": ""
|
| 530 |
+
},
|
| 531 |
+
{
|
| 532 |
+
"term": "engagement",
|
| 533 |
+
"category": "hard_skill",
|
| 534 |
+
"source": "resume_original",
|
| 535 |
+
"confidence": "high",
|
| 536 |
+
"usage_guidance": "safe_to_use",
|
| 537 |
+
"example_bullet": ""
|
| 538 |
+
},
|
| 539 |
+
{
|
| 540 |
+
"term": "use cases",
|
| 541 |
+
"category": "hard_skill",
|
| 542 |
+
"source": "inferred_plausible",
|
| 543 |
+
"confidence": "medium",
|
| 544 |
+
"usage_guidance": "use_carefully",
|
| 545 |
+
"example_bullet": ""
|
| 546 |
+
},
|
| 547 |
+
{
|
| 548 |
+
"term": "customer",
|
| 549 |
+
"category": "hard_skill",
|
| 550 |
+
"source": "resume_original",
|
| 551 |
+
"confidence": "high",
|
| 552 |
+
"usage_guidance": "safe_to_use",
|
| 553 |
+
"example_bullet": ""
|
| 554 |
+
},
|
| 555 |
+
{
|
| 556 |
+
"term": "dashboard",
|
| 557 |
+
"category": "hard_skill",
|
| 558 |
+
"source": "resume_original",
|
| 559 |
+
"confidence": "high",
|
| 560 |
+
"usage_guidance": "safe_to_use",
|
| 561 |
+
"example_bullet": ""
|
| 562 |
+
},
|
| 563 |
+
{
|
| 564 |
+
"term": "insights",
|
| 565 |
+
"category": "hard_skill",
|
| 566 |
+
"source": "inferred_plausible",
|
| 567 |
+
"confidence": "medium",
|
| 568 |
+
"usage_guidance": "use_carefully",
|
| 569 |
+
"example_bullet": ""
|
| 570 |
+
},
|
| 571 |
+
{
|
| 572 |
+
"term": "competitive",
|
| 573 |
+
"category": "hard_skill",
|
| 574 |
+
"source": "inferred_plausible",
|
| 575 |
+
"confidence": "medium",
|
| 576 |
+
"usage_guidance": "use_carefully",
|
| 577 |
+
"example_bullet": ""
|
| 578 |
+
},
|
| 579 |
+
{
|
| 580 |
+
"term": "vision",
|
| 581 |
+
"category": "hard_skill",
|
| 582 |
+
"source": "inferred_plausible",
|
| 583 |
+
"confidence": "medium",
|
| 584 |
+
"usage_guidance": "use_carefully",
|
| 585 |
+
"example_bullet": ""
|
| 586 |
+
},
|
| 587 |
+
{
|
| 588 |
+
"term": "requirements",
|
| 589 |
+
"category": "hard_skill",
|
| 590 |
+
"source": "inferred_plausible",
|
| 591 |
+
"confidence": "medium",
|
| 592 |
+
"usage_guidance": "use_carefully",
|
| 593 |
+
"example_bullet": ""
|
| 594 |
+
},
|
| 595 |
+
{
|
| 596 |
+
"term": "execution",
|
| 597 |
+
"category": "hard_skill",
|
| 598 |
+
"source": "resume_original",
|
| 599 |
+
"confidence": "high",
|
| 600 |
+
"usage_guidance": "safe_to_use",
|
| 601 |
+
"example_bullet": ""
|
| 602 |
+
},
|
| 603 |
+
{
|
| 604 |
+
"term": "features",
|
| 605 |
+
"category": "hard_skill",
|
| 606 |
+
"source": "resume_original",
|
| 607 |
+
"confidence": "high",
|
| 608 |
+
"usage_guidance": "safe_to_use",
|
| 609 |
+
"example_bullet": ""
|
| 610 |
+
},
|
| 611 |
+
{
|
| 612 |
+
"term": "scalable",
|
| 613 |
+
"category": "hard_skill",
|
| 614 |
+
"source": "inferred_plausible",
|
| 615 |
+
"confidence": "medium",
|
| 616 |
+
"usage_guidance": "use_carefully",
|
| 617 |
+
"example_bullet": ""
|
| 618 |
+
},
|
| 619 |
+
{
|
| 620 |
+
"term": "impact",
|
| 621 |
+
"category": "hard_skill",
|
| 622 |
+
"source": "inferred_plausible",
|
| 623 |
+
"confidence": "medium",
|
| 624 |
+
"usage_guidance": "use_carefully",
|
| 625 |
+
"example_bullet": ""
|
| 626 |
+
},
|
| 627 |
+
{
|
| 628 |
+
"term": "market",
|
| 629 |
+
"category": "hard_skill",
|
| 630 |
+
"source": "inferred_plausible",
|
| 631 |
+
"confidence": "medium",
|
| 632 |
+
"usage_guidance": "use_carefully",
|
| 633 |
+
"example_bullet": ""
|
| 634 |
+
},
|
| 635 |
+
{
|
| 636 |
+
"term": "reliability",
|
| 637 |
+
"category": "hard_skill",
|
| 638 |
+
"source": "inferred_plausible",
|
| 639 |
+
"confidence": "medium",
|
| 640 |
+
"usage_guidance": "use_carefully",
|
| 641 |
+
"example_bullet": ""
|
| 642 |
+
},
|
| 643 |
+
{
|
| 644 |
+
"term": "positioning",
|
| 645 |
+
"category": "hard_skill",
|
| 646 |
+
"source": "inferred_plausible",
|
| 647 |
+
"confidence": "medium",
|
| 648 |
+
"usage_guidance": "use_carefully",
|
| 649 |
+
"example_bullet": ""
|
| 650 |
+
},
|
| 651 |
+
{
|
| 652 |
+
"term": "operations platform",
|
| 653 |
+
"category": "hard_skill",
|
| 654 |
+
"source": "inferred_plausible",
|
| 655 |
+
"confidence": "medium",
|
| 656 |
+
"usage_guidance": "use_carefully",
|
| 657 |
+
"example_bullet": ""
|
| 658 |
+
},
|
| 659 |
+
{
|
| 660 |
+
"term": "product roadmap",
|
| 661 |
+
"category": "responsibility",
|
| 662 |
+
"source": "inferred_plausible",
|
| 663 |
+
"confidence": "medium",
|
| 664 |
+
"usage_guidance": "use_carefully",
|
| 665 |
+
"example_bullet": ""
|
| 666 |
+
},
|
| 667 |
+
{
|
| 668 |
+
"term": "siem",
|
| 669 |
+
"category": "domain",
|
| 670 |
+
"source": "risky_review",
|
| 671 |
+
"confidence": "low",
|
| 672 |
+
"usage_guidance": "ask_user",
|
| 673 |
+
"example_bullet": ""
|
| 674 |
+
},
|
| 675 |
+
{
|
| 676 |
+
"term": "xdr",
|
| 677 |
+
"category": "domain",
|
| 678 |
+
"source": "risky_review",
|
| 679 |
+
"confidence": "low",
|
| 680 |
+
"usage_guidance": "ask_user",
|
| 681 |
+
"example_bullet": ""
|
| 682 |
+
},
|
| 683 |
+
{
|
| 684 |
+
"term": "soar",
|
| 685 |
+
"category": "domain",
|
| 686 |
+
"source": "risky_review",
|
| 687 |
+
"confidence": "low",
|
| 688 |
+
"usage_guidance": "ask_user",
|
| 689 |
+
"example_bullet": ""
|
| 690 |
+
},
|
| 691 |
+
{
|
| 692 |
+
"term": "b2b",
|
| 693 |
+
"category": "domain",
|
| 694 |
+
"source": "inferred_plausible",
|
| 695 |
+
"confidence": "medium",
|
| 696 |
+
"usage_guidance": "use_carefully",
|
| 697 |
+
"example_bullet": ""
|
| 698 |
+
},
|
| 699 |
+
{
|
| 700 |
+
"term": "secops",
|
| 701 |
+
"category": "domain",
|
| 702 |
+
"source": "risky_review",
|
| 703 |
+
"confidence": "low",
|
| 704 |
+
"usage_guidance": "ask_user",
|
| 705 |
+
"example_bullet": ""
|
| 706 |
+
},
|
| 707 |
+
{
|
| 708 |
+
"term": "mba",
|
| 709 |
+
"category": "education",
|
| 710 |
+
"source": "inferred_plausible",
|
| 711 |
+
"confidence": "medium",
|
| 712 |
+
"usage_guidance": "use_carefully",
|
| 713 |
+
"example_bullet": ""
|
| 714 |
+
},
|
| 715 |
+
{
|
| 716 |
+
"term": "computer science",
|
| 717 |
+
"category": "education",
|
| 718 |
+
"source": "inferred_plausible",
|
| 719 |
+
"confidence": "medium",
|
| 720 |
+
"usage_guidance": "use_carefully",
|
| 721 |
+
"example_bullet": ""
|
| 722 |
+
},
|
| 723 |
+
{
|
| 724 |
+
"term": "3+ years",
|
| 725 |
+
"category": "seniority",
|
| 726 |
+
"source": "inferred_plausible",
|
| 727 |
+
"confidence": "medium",
|
| 728 |
+
"usage_guidance": "use_carefully",
|
| 729 |
+
"example_bullet": ""
|
| 730 |
+
},
|
| 731 |
+
{
|
| 732 |
+
"term": "microservices",
|
| 733 |
+
"category": "hard_skill",
|
| 734 |
+
"source": "inferred_plausible",
|
| 735 |
+
"confidence": "medium",
|
| 736 |
+
"usage_guidance": "use_carefully",
|
| 737 |
+
"example_bullet": ""
|
| 738 |
+
},
|
| 739 |
+
{
|
| 740 |
+
"term": "architecture",
|
| 741 |
+
"category": "hard_skill",
|
| 742 |
+
"source": "inferred_plausible",
|
| 743 |
+
"confidence": "medium",
|
| 744 |
+
"usage_guidance": "use_carefully",
|
| 745 |
+
"example_bullet": ""
|
| 746 |
+
},
|
| 747 |
+
{
|
| 748 |
+
"term": "system design",
|
| 749 |
+
"category": "hard_skill",
|
| 750 |
+
"source": "risky_review",
|
| 751 |
+
"confidence": "low",
|
| 752 |
+
"usage_guidance": "ask_user",
|
| 753 |
+
"example_bullet": ""
|
| 754 |
+
},
|
| 755 |
+
{
|
| 756 |
+
"term": "ci/cd",
|
| 757 |
+
"category": "hard_skill",
|
| 758 |
+
"source": "inferred_plausible",
|
| 759 |
+
"confidence": "medium",
|
| 760 |
+
"usage_guidance": "use_carefully",
|
| 761 |
+
"example_bullet": ""
|
| 762 |
+
},
|
| 763 |
+
{
|
| 764 |
+
"term": "graphql",
|
| 765 |
+
"category": "hard_skill",
|
| 766 |
+
"source": "inferred_plausible",
|
| 767 |
+
"confidence": "medium",
|
| 768 |
+
"usage_guidance": "use_carefully",
|
| 769 |
+
"example_bullet": ""
|
| 770 |
+
},
|
| 771 |
+
{
|
| 772 |
+
"term": "apis",
|
| 773 |
+
"category": "hard_skill",
|
| 774 |
+
"source": "inferred_plausible",
|
| 775 |
+
"confidence": "medium",
|
| 776 |
+
"usage_guidance": "use_carefully",
|
| 777 |
+
"example_bullet": ""
|
| 778 |
+
},
|
| 779 |
+
{
|
| 780 |
+
"term": "services",
|
| 781 |
+
"category": "hard_skill",
|
| 782 |
+
"source": "inferred_plausible",
|
| 783 |
+
"confidence": "medium",
|
| 784 |
+
"usage_guidance": "use_carefully",
|
| 785 |
+
"example_bullet": ""
|
| 786 |
+
},
|
| 787 |
+
{
|
| 788 |
+
"term": "pipelines",
|
| 789 |
+
"category": "hard_skill",
|
| 790 |
+
"source": "resume_original",
|
| 791 |
+
"confidence": "high",
|
| 792 |
+
"usage_guidance": "safe_to_use",
|
| 793 |
+
"example_bullet": ""
|
| 794 |
+
},
|
| 795 |
+
{
|
| 796 |
+
"term": "database",
|
| 797 |
+
"category": "hard_skill",
|
| 798 |
+
"source": "inferred_plausible",
|
| 799 |
+
"confidence": "medium",
|
| 800 |
+
"usage_guidance": "use_carefully",
|
| 801 |
+
"example_bullet": ""
|
| 802 |
+
},
|
| 803 |
+
{
|
| 804 |
+
"term": "latency",
|
| 805 |
+
"category": "hard_skill",
|
| 806 |
+
"source": "inferred_plausible",
|
| 807 |
+
"confidence": "medium",
|
| 808 |
+
"usage_guidance": "use_carefully",
|
| 809 |
+
"example_bullet": ""
|
| 810 |
+
},
|
| 811 |
+
{
|
| 812 |
+
"term": "optimization",
|
| 813 |
+
"category": "hard_skill",
|
| 814 |
+
"source": "resume_original",
|
| 815 |
+
"confidence": "high",
|
| 816 |
+
"usage_guidance": "safe_to_use",
|
| 817 |
+
"example_bullet": ""
|
| 818 |
+
},
|
| 819 |
+
{
|
| 820 |
+
"term": "backend software",
|
| 821 |
+
"category": "hard_skill",
|
| 822 |
+
"source": "inferred_plausible",
|
| 823 |
+
"confidence": "medium",
|
| 824 |
+
"usage_guidance": "use_carefully",
|
| 825 |
+
"example_bullet": ""
|
| 826 |
+
},
|
| 827 |
+
{
|
| 828 |
+
"term": "software engineer",
|
| 829 |
+
"category": "hard_skill",
|
| 830 |
+
"source": "inferred_plausible",
|
| 831 |
+
"confidence": "medium",
|
| 832 |
+
"usage_guidance": "use_carefully",
|
| 833 |
+
"example_bullet": ""
|
| 834 |
+
},
|
| 835 |
+
{
|
| 836 |
+
"term": "master's",
|
| 837 |
+
"category": "education",
|
| 838 |
+
"source": "inferred_plausible",
|
| 839 |
+
"confidence": "medium",
|
| 840 |
+
"usage_guidance": "use_carefully",
|
| 841 |
+
"example_bullet": ""
|
| 842 |
+
},
|
| 843 |
+
{
|
| 844 |
+
"term": "8+ years",
|
| 845 |
+
"category": "seniority",
|
| 846 |
+
"source": "blocked",
|
| 847 |
+
"confidence": "low",
|
| 848 |
+
"usage_guidance": "blocked",
|
| 849 |
+
"example_bullet": ""
|
| 850 |
+
},
|
| 851 |
+
{
|
| 852 |
+
"term": "senior",
|
| 853 |
+
"category": "seniority",
|
| 854 |
+
"source": "inferred_plausible",
|
| 855 |
+
"confidence": "medium",
|
| 856 |
+
"usage_guidance": "use_carefully",
|
| 857 |
+
"example_bullet": ""
|
| 858 |
+
},
|
| 859 |
+
{
|
| 860 |
+
"term": "platform",
|
| 861 |
+
"category": "hard_skill",
|
| 862 |
+
"source": "resume_original",
|
| 863 |
+
"confidence": "high",
|
| 864 |
+
"usage_guidance": "safe_to_use",
|
| 865 |
+
"example_bullet": ""
|
| 866 |
+
},
|
| 867 |
+
{
|
| 868 |
+
"term": "product strategy",
|
| 869 |
+
"category": "responsibility",
|
| 870 |
+
"source": "resume_original",
|
| 871 |
+
"confidence": "high",
|
| 872 |
+
"usage_guidance": "safe_to_use",
|
| 873 |
+
"example_bullet": ""
|
| 874 |
+
},
|
| 875 |
+
{
|
| 876 |
+
"term": "director",
|
| 877 |
+
"category": "seniority",
|
| 878 |
+
"source": "blocked",
|
| 879 |
+
"confidence": "low",
|
| 880 |
+
"usage_guidance": "blocked",
|
| 881 |
+
"example_bullet": ""
|
| 882 |
+
},
|
| 883 |
+
{
|
| 884 |
+
"term": "5+ years",
|
| 885 |
+
"category": "seniority",
|
| 886 |
+
"source": "resume_original",
|
| 887 |
+
"confidence": "high",
|
| 888 |
+
"usage_guidance": "safe_to_use",
|
| 889 |
+
"example_bullet": ""
|
| 890 |
+
},
|
| 891 |
+
{
|
| 892 |
+
"term": "vp",
|
| 893 |
+
"category": "seniority",
|
| 894 |
+
"source": "blocked",
|
| 895 |
+
"confidence": "low",
|
| 896 |
+
"usage_guidance": "blocked",
|
| 897 |
+
"example_bullet": ""
|
| 898 |
+
},
|
| 899 |
+
{
|
| 900 |
+
"term": "12+ years",
|
| 901 |
+
"category": "seniority",
|
| 902 |
+
"source": "blocked",
|
| 903 |
+
"confidence": "low",
|
| 904 |
+
"usage_guidance": "blocked",
|
| 905 |
+
"example_bullet": ""
|
| 906 |
+
},
|
| 907 |
+
{
|
| 908 |
+
"term": "customer empathy",
|
| 909 |
+
"category": "hard_skill",
|
| 910 |
+
"source": "inferred_plausible",
|
| 911 |
+
"confidence": "medium",
|
| 912 |
+
"usage_guidance": "use_carefully",
|
| 913 |
+
"example_bullet": ""
|
| 914 |
+
},
|
| 915 |
+
{
|
| 916 |
+
"term": "outcomes",
|
| 917 |
+
"category": "hard_skill",
|
| 918 |
+
"source": "resume_original",
|
| 919 |
+
"confidence": "high",
|
| 920 |
+
"usage_guidance": "safe_to_use",
|
| 921 |
+
"example_bullet": ""
|
| 922 |
+
},
|
| 923 |
+
{
|
| 924 |
+
"term": "funnel",
|
| 925 |
+
"category": "hard_skill",
|
| 926 |
+
"source": "resume_original",
|
| 927 |
+
"confidence": "high",
|
| 928 |
+
"usage_guidance": "safe_to_use",
|
| 929 |
+
"example_bullet": ""
|
| 930 |
+
},
|
| 931 |
+
{
|
| 932 |
+
"term": "analysis",
|
| 933 |
+
"category": "hard_skill",
|
| 934 |
+
"source": "inferred_plausible",
|
| 935 |
+
"confidence": "medium",
|
| 936 |
+
"usage_guidance": "use_carefully",
|
| 937 |
+
"example_bullet": ""
|
| 938 |
+
},
|
| 939 |
+
{
|
| 940 |
+
"term": "competitive benchmarking",
|
| 941 |
+
"category": "hard_skill",
|
| 942 |
+
"source": "inferred_plausible",
|
| 943 |
+
"confidence": "medium",
|
| 944 |
+
"usage_guidance": "use_carefully",
|
| 945 |
+
"example_bullet": ""
|
| 946 |
+
},
|
| 947 |
+
{
|
| 948 |
+
"term": "acceptance criteria",
|
| 949 |
+
"category": "hard_skill",
|
| 950 |
+
"source": "resume_original",
|
| 951 |
+
"confidence": "high",
|
| 952 |
+
"usage_guidance": "safe_to_use",
|
| 953 |
+
"example_bullet": ""
|
| 954 |
+
},
|
| 955 |
+
{
|
| 956 |
+
"term": "usability testing",
|
| 957 |
+
"category": "hard_skill",
|
| 958 |
+
"source": "inferred_plausible",
|
| 959 |
+
"confidence": "medium",
|
| 960 |
+
"usage_guidance": "use_carefully",
|
| 961 |
+
"example_bullet": ""
|
| 962 |
+
},
|
| 963 |
+
{
|
| 964 |
+
"term": "mlops",
|
| 965 |
+
"category": "hard_skill",
|
| 966 |
+
"source": "inferred_plausible",
|
| 967 |
+
"confidence": "medium",
|
| 968 |
+
"usage_guidance": "use_carefully",
|
| 969 |
+
"example_bullet": ""
|
| 970 |
+
},
|
| 971 |
+
{
|
| 972 |
+
"term": "growth",
|
| 973 |
+
"category": "hard_skill",
|
| 974 |
+
"source": "resume_original",
|
| 975 |
+
"confidence": "high",
|
| 976 |
+
"usage_guidance": "safe_to_use",
|
| 977 |
+
"example_bullet": ""
|
| 978 |
+
},
|
| 979 |
+
{
|
| 980 |
+
"term": "backlog",
|
| 981 |
+
"category": "hard_skill",
|
| 982 |
+
"source": "inferred_plausible",
|
| 983 |
+
"confidence": "medium",
|
| 984 |
+
"usage_guidance": "use_carefully",
|
| 985 |
+
"example_bullet": ""
|
| 986 |
+
},
|
| 987 |
+
{
|
| 988 |
+
"term": "operations",
|
| 989 |
+
"category": "hard_skill",
|
| 990 |
+
"source": "inferred_plausible",
|
| 991 |
+
"confidence": "medium",
|
| 992 |
+
"usage_guidance": "use_carefully",
|
| 993 |
+
"example_bullet": ""
|
| 994 |
+
},
|
| 995 |
+
{
|
| 996 |
+
"term": "hypothesis",
|
| 997 |
+
"category": "hard_skill",
|
| 998 |
+
"source": "inferred_plausible",
|
| 999 |
+
"confidence": "medium",
|
| 1000 |
+
"usage_guidance": "use_carefully",
|
| 1001 |
+
"example_bullet": ""
|
| 1002 |
+
},
|
| 1003 |
+
{
|
| 1004 |
+
"term": "governance",
|
| 1005 |
+
"category": "hard_skill",
|
| 1006 |
+
"source": "inferred_plausible",
|
| 1007 |
+
"confidence": "medium",
|
| 1008 |
+
"usage_guidance": "use_carefully",
|
| 1009 |
+
"example_bullet": ""
|
| 1010 |
+
},
|
| 1011 |
+
{
|
| 1012 |
+
"term": "collaborate",
|
| 1013 |
+
"category": "hard_skill",
|
| 1014 |
+
"source": "inferred_plausible",
|
| 1015 |
+
"confidence": "medium",
|
| 1016 |
+
"usage_guidance": "use_carefully",
|
| 1017 |
+
"example_bullet": ""
|
| 1018 |
+
},
|
| 1019 |
+
{
|
| 1020 |
+
"term": "system",
|
| 1021 |
+
"category": "hard_skill",
|
| 1022 |
+
"source": "resume_original",
|
| 1023 |
+
"confidence": "high",
|
| 1024 |
+
"usage_guidance": "safe_to_use",
|
| 1025 |
+
"example_bullet": ""
|
| 1026 |
+
},
|
| 1027 |
+
{
|
| 1028 |
+
"term": "cross-functional collaboration",
|
| 1029 |
+
"category": "hard_skill",
|
| 1030 |
+
"source": "resume_original",
|
| 1031 |
+
"confidence": "high",
|
| 1032 |
+
"usage_guidance": "safe_to_use",
|
| 1033 |
+
"example_bullet": ""
|
| 1034 |
+
},
|
| 1035 |
+
{
|
| 1036 |
+
"term": "experiments",
|
| 1037 |
+
"category": "hard_skill",
|
| 1038 |
+
"source": "resume_original",
|
| 1039 |
+
"confidence": "high",
|
| 1040 |
+
"usage_guidance": "safe_to_use",
|
| 1041 |
+
"example_bullet": ""
|
| 1042 |
+
},
|
| 1043 |
+
{
|
| 1044 |
+
"term": "iterative",
|
| 1045 |
+
"category": "hard_skill",
|
| 1046 |
+
"source": "inferred_plausible",
|
| 1047 |
+
"confidence": "medium",
|
| 1048 |
+
"usage_guidance": "use_carefully",
|
| 1049 |
+
"example_bullet": ""
|
| 1050 |
+
},
|
| 1051 |
+
{
|
| 1052 |
+
"term": "product analytics",
|
| 1053 |
+
"category": "hard_skill",
|
| 1054 |
+
"source": "inferred_plausible",
|
| 1055 |
+
"confidence": "medium",
|
| 1056 |
+
"usage_guidance": "use_carefully",
|
| 1057 |
+
"example_bullet": ""
|
| 1058 |
+
},
|
| 1059 |
+
{
|
| 1060 |
+
"term": "sprint planning",
|
| 1061 |
+
"category": "responsibility",
|
| 1062 |
+
"source": "inferred_plausible",
|
| 1063 |
+
"confidence": "medium",
|
| 1064 |
+
"usage_guidance": "use_carefully",
|
| 1065 |
+
"example_bullet": ""
|
| 1066 |
+
},
|
| 1067 |
+
{
|
| 1068 |
+
"term": "user stories",
|
| 1069 |
+
"category": "responsibility",
|
| 1070 |
+
"source": "resume_original",
|
| 1071 |
+
"confidence": "high",
|
| 1072 |
+
"usage_guidance": "safe_to_use",
|
| 1073 |
+
"example_bullet": ""
|
| 1074 |
+
},
|
| 1075 |
+
{
|
| 1076 |
+
"term": "banking",
|
| 1077 |
+
"category": "domain",
|
| 1078 |
+
"source": "inferred_plausible",
|
| 1079 |
+
"confidence": "medium",
|
| 1080 |
+
"usage_guidance": "use_carefully",
|
| 1081 |
+
"example_bullet": ""
|
| 1082 |
+
},
|
| 1083 |
+
{
|
| 1084 |
+
"term": "problem solving",
|
| 1085 |
+
"category": "soft_skill",
|
| 1086 |
+
"source": "inferred_plausible",
|
| 1087 |
+
"confidence": "medium",
|
| 1088 |
+
"usage_guidance": "use_carefully",
|
| 1089 |
+
"example_bullet": ""
|
| 1090 |
+
},
|
| 1091 |
+
{
|
| 1092 |
+
"term": "6 years",
|
| 1093 |
+
"category": "seniority",
|
| 1094 |
+
"source": "inferred_plausible",
|
| 1095 |
+
"confidence": "medium",
|
| 1096 |
+
"usage_guidance": "use_carefully",
|
| 1097 |
+
"example_bullet": ""
|
| 1098 |
+
},
|
| 1099 |
+
{
|
| 1100 |
+
"term": "artificial intelligence",
|
| 1101 |
+
"category": "hard_skill",
|
| 1102 |
+
"source": "inferred_plausible",
|
| 1103 |
+
"confidence": "medium",
|
| 1104 |
+
"usage_guidance": "use_carefully",
|
| 1105 |
+
"example_bullet": ""
|
| 1106 |
+
},
|
| 1107 |
+
{
|
| 1108 |
+
"term": "foundation models",
|
| 1109 |
+
"category": "hard_skill",
|
| 1110 |
+
"source": "inferred_plausible",
|
| 1111 |
+
"confidence": "medium",
|
| 1112 |
+
"usage_guidance": "use_carefully",
|
| 1113 |
+
"example_bullet": ""
|
| 1114 |
+
},
|
| 1115 |
+
{
|
| 1116 |
+
"term": "machine learning",
|
| 1117 |
+
"category": "hard_skill",
|
| 1118 |
+
"source": "resume_original",
|
| 1119 |
+
"confidence": "high",
|
| 1120 |
+
"usage_guidance": "safe_to_use",
|
| 1121 |
+
"example_bullet": ""
|
| 1122 |
+
},
|
| 1123 |
+
{
|
| 1124 |
+
"term": "model validation",
|
| 1125 |
+
"category": "hard_skill",
|
| 1126 |
+
"source": "inferred_plausible",
|
| 1127 |
+
"confidence": "medium",
|
| 1128 |
+
"usage_guidance": "use_carefully",
|
| 1129 |
+
"example_bullet": ""
|
| 1130 |
+
},
|
| 1131 |
+
{
|
| 1132 |
+
"term": "generative ai",
|
| 1133 |
+
"category": "hard_skill",
|
| 1134 |
+
"source": "resume_original",
|
| 1135 |
+
"confidence": "high",
|
| 1136 |
+
"usage_guidance": "safe_to_use",
|
| 1137 |
+
"example_bullet": ""
|
| 1138 |
+
},
|
| 1139 |
+
{
|
| 1140 |
+
"term": "data science",
|
| 1141 |
+
"category": "hard_skill",
|
| 1142 |
+
"source": "inferred_plausible",
|
| 1143 |
+
"confidence": "medium",
|
| 1144 |
+
"usage_guidance": "use_carefully",
|
| 1145 |
+
"example_bullet": ""
|
| 1146 |
+
},
|
| 1147 |
+
{
|
| 1148 |
+
"term": "experimentation",
|
| 1149 |
+
"category": "hard_skill",
|
| 1150 |
+
"source": "resume_original",
|
| 1151 |
+
"confidence": "high",
|
| 1152 |
+
"usage_guidance": "safe_to_use",
|
| 1153 |
+
"example_bullet": ""
|
| 1154 |
+
},
|
| 1155 |
+
{
|
| 1156 |
+
"term": "support",
|
| 1157 |
+
"category": "hard_skill",
|
| 1158 |
+
"source": "inferred_plausible",
|
| 1159 |
+
"confidence": "medium",
|
| 1160 |
+
"usage_guidance": "use_carefully",
|
| 1161 |
+
"example_bullet": ""
|
| 1162 |
+
},
|
| 1163 |
+
{
|
| 1164 |
+
"term": "infrastructure",
|
| 1165 |
+
"category": "hard_skill",
|
| 1166 |
+
"source": "inferred_plausible",
|
| 1167 |
+
"confidence": "medium",
|
| 1168 |
+
"usage_guidance": "use_carefully",
|
| 1169 |
+
"example_bullet": ""
|
| 1170 |
+
},
|
| 1171 |
+
{
|
| 1172 |
+
"term": "prototypes",
|
| 1173 |
+
"category": "hard_skill",
|
| 1174 |
+
"source": "inferred_plausible",
|
| 1175 |
+
"confidence": "medium",
|
| 1176 |
+
"usage_guidance": "use_carefully",
|
| 1177 |
+
"example_bullet": ""
|
| 1178 |
+
},
|
| 1179 |
+
{
|
| 1180 |
+
"term": "applications",
|
| 1181 |
+
"category": "hard_skill",
|
| 1182 |
+
"source": "resume_original",
|
| 1183 |
+
"confidence": "high",
|
| 1184 |
+
"usage_guidance": "safe_to_use",
|
| 1185 |
+
"example_bullet": ""
|
| 1186 |
+
},
|
| 1187 |
+
{
|
| 1188 |
+
"term": "initiatives",
|
| 1189 |
+
"category": "hard_skill",
|
| 1190 |
+
"source": "inferred_plausible",
|
| 1191 |
+
"confidence": "medium",
|
| 1192 |
+
"usage_guidance": "use_carefully",
|
| 1193 |
+
"example_bullet": ""
|
| 1194 |
+
},
|
| 1195 |
+
{
|
| 1196 |
+
"term": "research",
|
| 1197 |
+
"category": "hard_skill",
|
| 1198 |
+
"source": "resume_original",
|
| 1199 |
+
"confidence": "high",
|
| 1200 |
+
"usage_guidance": "safe_to_use",
|
| 1201 |
+
"example_bullet": ""
|
| 1202 |
+
},
|
| 1203 |
+
{
|
| 1204 |
+
"term": "5 to 9",
|
| 1205 |
+
"category": "hard_skill",
|
| 1206 |
+
"source": "inferred_plausible",
|
| 1207 |
+
"confidence": "medium",
|
| 1208 |
+
"usage_guidance": "use_carefully",
|
| 1209 |
+
"example_bullet": ""
|
| 1210 |
+
},
|
| 1211 |
+
{
|
| 1212 |
+
"term": "9 years",
|
| 1213 |
+
"category": "seniority",
|
| 1214 |
+
"source": "blocked",
|
| 1215 |
+
"confidence": "low",
|
| 1216 |
+
"usage_guidance": "blocked",
|
| 1217 |
+
"example_bullet": ""
|
| 1218 |
+
},
|
| 1219 |
+
{
|
| 1220 |
+
"term": "4+ years",
|
| 1221 |
+
"category": "seniority",
|
| 1222 |
+
"source": "inferred_plausible",
|
| 1223 |
+
"confidence": "medium",
|
| 1224 |
+
"usage_guidance": "use_carefully",
|
| 1225 |
+
"example_bullet": ""
|
| 1226 |
+
},
|
| 1227 |
+
{
|
| 1228 |
+
"term": "user testing",
|
| 1229 |
+
"category": "hard_skill",
|
| 1230 |
+
"source": "inferred_plausible",
|
| 1231 |
+
"confidence": "medium",
|
| 1232 |
+
"usage_guidance": "use_carefully",
|
| 1233 |
+
"example_bullet": ""
|
| 1234 |
+
},
|
| 1235 |
+
{
|
| 1236 |
+
"term": "users",
|
| 1237 |
+
"category": "hard_skill",
|
| 1238 |
+
"source": "resume_original",
|
| 1239 |
+
"confidence": "high",
|
| 1240 |
+
"usage_guidance": "safe_to_use",
|
| 1241 |
+
"example_bullet": ""
|
| 1242 |
+
},
|
| 1243 |
+
{
|
| 1244 |
+
"term": "technology",
|
| 1245 |
+
"category": "hard_skill",
|
| 1246 |
+
"source": "resume_original",
|
| 1247 |
+
"confidence": "high",
|
| 1248 |
+
"usage_guidance": "safe_to_use",
|
| 1249 |
+
"example_bullet": ""
|
| 1250 |
+
},
|
| 1251 |
+
{
|
| 1252 |
+
"term": "influence",
|
| 1253 |
+
"category": "hard_skill",
|
| 1254 |
+
"source": "inferred_plausible",
|
| 1255 |
+
"confidence": "medium",
|
| 1256 |
+
"usage_guidance": "use_carefully",
|
| 1257 |
+
"example_bullet": ""
|
| 1258 |
+
},
|
| 1259 |
+
{
|
| 1260 |
+
"term": "business objectives",
|
| 1261 |
+
"category": "hard_skill",
|
| 1262 |
+
"source": "inferred_plausible",
|
| 1263 |
+
"confidence": "medium",
|
| 1264 |
+
"usage_guidance": "use_carefully",
|
| 1265 |
+
"example_bullet": ""
|
| 1266 |
+
},
|
| 1267 |
+
{
|
| 1268 |
+
"term": "administration",
|
| 1269 |
+
"category": "hard_skill",
|
| 1270 |
+
"source": "inferred_plausible",
|
| 1271 |
+
"confidence": "medium",
|
| 1272 |
+
"usage_guidance": "use_carefully",
|
| 1273 |
+
"example_bullet": ""
|
| 1274 |
+
},
|
| 1275 |
+
{
|
| 1276 |
+
"term": "quantitative",
|
| 1277 |
+
"category": "hard_skill",
|
| 1278 |
+
"source": "inferred_plausible",
|
| 1279 |
+
"confidence": "medium",
|
| 1280 |
+
"usage_guidance": "use_carefully",
|
| 1281 |
+
"example_bullet": ""
|
| 1282 |
+
},
|
| 1283 |
+
{
|
| 1284 |
+
"term": "engineering design",
|
| 1285 |
+
"category": "hard_skill",
|
| 1286 |
+
"source": "inferred_plausible",
|
| 1287 |
+
"confidence": "medium",
|
| 1288 |
+
"usage_guidance": "use_carefully",
|
| 1289 |
+
"example_bullet": ""
|
| 1290 |
+
},
|
| 1291 |
+
{
|
| 1292 |
+
"term": "design data",
|
| 1293 |
+
"category": "hard_skill",
|
| 1294 |
+
"source": "inferred_plausible",
|
| 1295 |
+
"confidence": "medium",
|
| 1296 |
+
"usage_guidance": "use_carefully",
|
| 1297 |
+
"example_bullet": ""
|
| 1298 |
+
},
|
| 1299 |
+
{
|
| 1300 |
+
"term": "insurance",
|
| 1301 |
+
"category": "domain",
|
| 1302 |
+
"source": "inferred_plausible",
|
| 1303 |
+
"confidence": "medium",
|
| 1304 |
+
"usage_guidance": "use_carefully",
|
| 1305 |
+
"example_bullet": ""
|
| 1306 |
+
},
|
| 1307 |
+
{
|
| 1308 |
+
"term": "automation",
|
| 1309 |
+
"category": "hard_skill",
|
| 1310 |
+
"source": "resume_original",
|
| 1311 |
+
"confidence": "high",
|
| 1312 |
+
"usage_guidance": "safe_to_use",
|
| 1313 |
+
"example_bullet": ""
|
| 1314 |
+
},
|
| 1315 |
+
{
|
| 1316 |
+
"term": "partners",
|
| 1317 |
+
"category": "hard_skill",
|
| 1318 |
+
"source": "resume_original",
|
| 1319 |
+
"confidence": "high",
|
| 1320 |
+
"usage_guidance": "safe_to_use",
|
| 1321 |
+
"example_bullet": ""
|
| 1322 |
+
},
|
| 1323 |
+
{
|
| 1324 |
+
"term": "processes",
|
| 1325 |
+
"category": "hard_skill",
|
| 1326 |
+
"source": "resume_original",
|
| 1327 |
+
"confidence": "high",
|
| 1328 |
+
"usage_guidance": "safe_to_use",
|
| 1329 |
+
"example_bullet": ""
|
| 1330 |
+
},
|
| 1331 |
+
{
|
| 1332 |
+
"term": "monitoring",
|
| 1333 |
+
"category": "hard_skill",
|
| 1334 |
+
"source": "resume_original",
|
| 1335 |
+
"confidence": "high",
|
| 1336 |
+
"usage_guidance": "safe_to_use",
|
| 1337 |
+
"example_bullet": ""
|
| 1338 |
+
},
|
| 1339 |
+
{
|
| 1340 |
+
"term": "process",
|
| 1341 |
+
"category": "hard_skill",
|
| 1342 |
+
"source": "resume_original",
|
| 1343 |
+
"confidence": "high",
|
| 1344 |
+
"usage_guidance": "safe_to_use",
|
| 1345 |
+
"example_bullet": ""
|
| 1346 |
+
},
|
| 1347 |
+
{
|
| 1348 |
+
"term": "reporting",
|
| 1349 |
+
"category": "hard_skill",
|
| 1350 |
+
"source": "resume_original",
|
| 1351 |
+
"confidence": "high",
|
| 1352 |
+
"usage_guidance": "safe_to_use",
|
| 1353 |
+
"example_bullet": ""
|
| 1354 |
+
},
|
| 1355 |
+
{
|
| 1356 |
+
"term": "platforms",
|
| 1357 |
+
"category": "hard_skill",
|
| 1358 |
+
"source": "resume_original",
|
| 1359 |
+
"confidence": "high",
|
| 1360 |
+
"usage_guidance": "safe_to_use",
|
| 1361 |
+
"example_bullet": ""
|
| 1362 |
+
},
|
| 1363 |
+
{
|
| 1364 |
+
"term": "workflows",
|
| 1365 |
+
"category": "hard_skill",
|
| 1366 |
+
"source": "inferred_plausible",
|
| 1367 |
+
"confidence": "medium",
|
| 1368 |
+
"usage_guidance": "use_carefully",
|
| 1369 |
+
"example_bullet": ""
|
| 1370 |
+
},
|
| 1371 |
+
{
|
| 1372 |
+
"term": "fintech e-commerce",
|
| 1373 |
+
"category": "hard_skill",
|
| 1374 |
+
"source": "inferred_plausible",
|
| 1375 |
+
"confidence": "medium",
|
| 1376 |
+
"usage_guidance": "use_carefully",
|
| 1377 |
+
"example_bullet": ""
|
| 1378 |
+
},
|
| 1379 |
+
{
|
| 1380 |
+
"term": "sql",
|
| 1381 |
+
"category": "tool",
|
| 1382 |
+
"source": "inferred_plausible",
|
| 1383 |
+
"confidence": "medium",
|
| 1384 |
+
"usage_guidance": "use_carefully",
|
| 1385 |
+
"example_bullet": ""
|
| 1386 |
+
},
|
| 1387 |
+
{
|
| 1388 |
+
"term": "excel",
|
| 1389 |
+
"category": "tool",
|
| 1390 |
+
"source": "inferred_plausible",
|
| 1391 |
+
"confidence": "medium",
|
| 1392 |
+
"usage_guidance": "use_carefully",
|
| 1393 |
+
"example_bullet": ""
|
| 1394 |
+
},
|
| 1395 |
+
{
|
| 1396 |
+
"term": "payments",
|
| 1397 |
+
"category": "domain",
|
| 1398 |
+
"source": "resume_original",
|
| 1399 |
+
"confidence": "high",
|
| 1400 |
+
"usage_guidance": "safe_to_use",
|
| 1401 |
+
"example_bullet": ""
|
| 1402 |
+
},
|
| 1403 |
+
{
|
| 1404 |
+
"term": "4 years",
|
| 1405 |
+
"category": "seniority",
|
| 1406 |
+
"source": "inferred_plausible",
|
| 1407 |
+
"confidence": "medium",
|
| 1408 |
+
"usage_guidance": "use_carefully",
|
| 1409 |
+
"example_bullet": ""
|
| 1410 |
+
},
|
| 1411 |
+
{
|
| 1412 |
+
"term": "3 years",
|
| 1413 |
+
"category": "seniority",
|
| 1414 |
+
"source": "inferred_plausible",
|
| 1415 |
+
"confidence": "medium",
|
| 1416 |
+
"usage_guidance": "use_carefully",
|
| 1417 |
+
"example_bullet": ""
|
| 1418 |
+
},
|
| 1419 |
+
{
|
| 1420 |
+
"term": "associate",
|
| 1421 |
+
"category": "seniority",
|
| 1422 |
+
"source": "inferred_plausible",
|
| 1423 |
+
"confidence": "medium",
|
| 1424 |
+
"usage_guidance": "use_carefully",
|
| 1425 |
+
"example_bullet": ""
|
| 1426 |
+
},
|
| 1427 |
+
{
|
| 1428 |
+
"term": "functional specification",
|
| 1429 |
+
"category": "hard_skill",
|
| 1430 |
+
"source": "inferred_plausible",
|
| 1431 |
+
"confidence": "medium",
|
| 1432 |
+
"usage_guidance": "use_carefully",
|
| 1433 |
+
"example_bullet": ""
|
| 1434 |
+
},
|
| 1435 |
+
{
|
| 1436 |
+
"term": "requirements elicitation",
|
| 1437 |
+
"category": "hard_skill",
|
| 1438 |
+
"source": "inferred_plausible",
|
| 1439 |
+
"confidence": "medium",
|
| 1440 |
+
"usage_guidance": "use_carefully",
|
| 1441 |
+
"example_bullet": ""
|
| 1442 |
+
},
|
| 1443 |
+
{
|
| 1444 |
+
"term": "test plans",
|
| 1445 |
+
"category": "hard_skill",
|
| 1446 |
+
"source": "inferred_plausible",
|
| 1447 |
+
"confidence": "medium",
|
| 1448 |
+
"usage_guidance": "use_carefully",
|
| 1449 |
+
"example_bullet": ""
|
| 1450 |
+
},
|
| 1451 |
+
{
|
| 1452 |
+
"term": "uat",
|
| 1453 |
+
"category": "hard_skill",
|
| 1454 |
+
"source": "inferred_plausible",
|
| 1455 |
+
"confidence": "medium",
|
| 1456 |
+
"usage_guidance": "use_carefully",
|
| 1457 |
+
"example_bullet": ""
|
| 1458 |
+
},
|
| 1459 |
+
{
|
| 1460 |
+
"term": "documentation",
|
| 1461 |
+
"category": "hard_skill",
|
| 1462 |
+
"source": "resume_original",
|
| 1463 |
+
"confidence": "high",
|
| 1464 |
+
"usage_guidance": "safe_to_use",
|
| 1465 |
+
"example_bullet": ""
|
| 1466 |
+
},
|
| 1467 |
+
{
|
| 1468 |
+
"term": "fsd",
|
| 1469 |
+
"category": "hard_skill",
|
| 1470 |
+
"source": "inferred_plausible",
|
| 1471 |
+
"confidence": "medium",
|
| 1472 |
+
"usage_guidance": "use_carefully",
|
| 1473 |
+
"example_bullet": ""
|
| 1474 |
+
},
|
| 1475 |
+
{
|
| 1476 |
+
"term": "scope",
|
| 1477 |
+
"category": "hard_skill",
|
| 1478 |
+
"source": "inferred_plausible",
|
| 1479 |
+
"confidence": "medium",
|
| 1480 |
+
"usage_guidance": "use_carefully",
|
| 1481 |
+
"example_bullet": ""
|
| 1482 |
+
},
|
| 1483 |
+
{
|
| 1484 |
+
"term": "projects",
|
| 1485 |
+
"category": "hard_skill",
|
| 1486 |
+
"source": "resume_original",
|
| 1487 |
+
"confidence": "high",
|
| 1488 |
+
"usage_guidance": "safe_to_use",
|
| 1489 |
+
"example_bullet": ""
|
| 1490 |
+
},
|
| 1491 |
+
{
|
| 1492 |
+
"term": "implementation",
|
| 1493 |
+
"category": "hard_skill",
|
| 1494 |
+
"source": "resume_original",
|
| 1495 |
+
"confidence": "high",
|
| 1496 |
+
"usage_guidance": "safe_to_use",
|
| 1497 |
+
"example_bullet": ""
|
| 1498 |
+
},
|
| 1499 |
+
{
|
| 1500 |
+
"term": "team",
|
| 1501 |
+
"category": "hard_skill",
|
| 1502 |
+
"source": "resume_original",
|
| 1503 |
+
"confidence": "high",
|
| 1504 |
+
"usage_guidance": "safe_to_use",
|
| 1505 |
+
"example_bullet": ""
|
| 1506 |
+
},
|
| 1507 |
+
{
|
| 1508 |
+
"term": "timelines",
|
| 1509 |
+
"category": "hard_skill",
|
| 1510 |
+
"source": "inferred_plausible",
|
| 1511 |
+
"confidence": "medium",
|
| 1512 |
+
"usage_guidance": "use_carefully",
|
| 1513 |
+
"example_bullet": ""
|
| 1514 |
+
},
|
| 1515 |
+
{
|
| 1516 |
+
"term": "coordination",
|
| 1517 |
+
"category": "hard_skill",
|
| 1518 |
+
"source": "inferred_plausible",
|
| 1519 |
+
"confidence": "medium",
|
| 1520 |
+
"usage_guidance": "use_carefully",
|
| 1521 |
+
"example_bullet": ""
|
| 1522 |
+
},
|
| 1523 |
+
{
|
| 1524 |
+
"term": "elicitation design",
|
| 1525 |
+
"category": "hard_skill",
|
| 1526 |
+
"source": "inferred_plausible",
|
| 1527 |
+
"confidence": "medium",
|
| 1528 |
+
"usage_guidance": "use_carefully",
|
| 1529 |
+
"example_bullet": ""
|
| 1530 |
+
},
|
| 1531 |
+
{
|
| 1532 |
+
"term": "user acceptance testing",
|
| 1533 |
+
"category": "responsibility",
|
| 1534 |
+
"source": "resume_original",
|
| 1535 |
+
"confidence": "high",
|
| 1536 |
+
"usage_guidance": "safe_to_use",
|
| 1537 |
+
"example_bullet": ""
|
| 1538 |
+
},
|
| 1539 |
+
{
|
| 1540 |
+
"term": "go-to-market strategy",
|
| 1541 |
+
"category": "hard_skill",
|
| 1542 |
+
"source": "inferred_plausible",
|
| 1543 |
+
"confidence": "medium",
|
| 1544 |
+
"usage_guidance": "use_carefully",
|
| 1545 |
+
"example_bullet": ""
|
| 1546 |
+
},
|
| 1547 |
+
{
|
| 1548 |
+
"term": "prds",
|
| 1549 |
+
"category": "hard_skill",
|
| 1550 |
+
"source": "inferred_plausible",
|
| 1551 |
+
"confidence": "medium",
|
| 1552 |
+
"usage_guidance": "use_carefully",
|
| 1553 |
+
"example_bullet": ""
|
| 1554 |
+
},
|
| 1555 |
+
{
|
| 1556 |
+
"term": "ceremonies",
|
| 1557 |
+
"category": "hard_skill",
|
| 1558 |
+
"source": "inferred_plausible",
|
| 1559 |
+
"confidence": "medium",
|
| 1560 |
+
"usage_guidance": "use_carefully",
|
| 1561 |
+
"example_bullet": ""
|
| 1562 |
+
},
|
| 1563 |
+
{
|
| 1564 |
+
"term": "adoption",
|
| 1565 |
+
"category": "hard_skill",
|
| 1566 |
+
"source": "inferred_plausible",
|
| 1567 |
+
"confidence": "medium",
|
| 1568 |
+
"usage_guidance": "use_carefully",
|
| 1569 |
+
"example_bullet": ""
|
| 1570 |
+
},
|
| 1571 |
+
{
|
| 1572 |
+
"term": "okr",
|
| 1573 |
+
"category": "hard_skill",
|
| 1574 |
+
"source": "inferred_plausible",
|
| 1575 |
+
"confidence": "medium",
|
| 1576 |
+
"usage_guidance": "use_carefully",
|
| 1577 |
+
"example_bullet": ""
|
| 1578 |
+
},
|
| 1579 |
+
{
|
| 1580 |
+
"term": "heap",
|
| 1581 |
+
"category": "hard_skill",
|
| 1582 |
+
"source": "inferred_plausible",
|
| 1583 |
+
"confidence": "medium",
|
| 1584 |
+
"usage_guidance": "use_carefully",
|
| 1585 |
+
"example_bullet": ""
|
| 1586 |
+
},
|
| 1587 |
+
{
|
| 1588 |
+
"term": "retention",
|
| 1589 |
+
"category": "hard_skill",
|
| 1590 |
+
"source": "resume_original",
|
| 1591 |
+
"confidence": "high",
|
| 1592 |
+
"usage_guidance": "safe_to_use",
|
| 1593 |
+
"example_bullet": ""
|
| 1594 |
+
},
|
| 1595 |
+
{
|
| 1596 |
+
"term": "activation",
|
| 1597 |
+
"category": "hard_skill",
|
| 1598 |
+
"source": "inferred_plausible",
|
| 1599 |
+
"confidence": "medium",
|
| 1600 |
+
"usage_guidance": "use_carefully",
|
| 1601 |
+
"example_bullet": ""
|
| 1602 |
+
},
|
| 1603 |
+
{
|
| 1604 |
+
"term": "recommendation",
|
| 1605 |
+
"category": "hard_skill",
|
| 1606 |
+
"source": "inferred_plausible",
|
| 1607 |
+
"confidence": "medium",
|
| 1608 |
+
"usage_guidance": "use_carefully",
|
| 1609 |
+
"example_bullet": ""
|
| 1610 |
+
},
|
| 1611 |
+
{
|
| 1612 |
+
"term": "onboarding",
|
| 1613 |
+
"category": "hard_skill",
|
| 1614 |
+
"source": "resume_original",
|
| 1615 |
+
"confidence": "high",
|
| 1616 |
+
"usage_guidance": "safe_to_use",
|
| 1617 |
+
"example_bullet": ""
|
| 1618 |
+
},
|
| 1619 |
+
{
|
| 1620 |
+
"term": "personalization",
|
| 1621 |
+
"category": "hard_skill",
|
| 1622 |
+
"source": "inferred_plausible",
|
| 1623 |
+
"confidence": "medium",
|
| 1624 |
+
"usage_guidance": "use_carefully",
|
| 1625 |
+
"example_bullet": ""
|
| 1626 |
+
},
|
| 1627 |
+
{
|
| 1628 |
+
"term": "frameworks",
|
| 1629 |
+
"category": "hard_skill",
|
| 1630 |
+
"source": "inferred_plausible",
|
| 1631 |
+
"confidence": "medium",
|
| 1632 |
+
"usage_guidance": "use_carefully",
|
| 1633 |
+
"example_bullet": ""
|
| 1634 |
+
},
|
| 1635 |
+
{
|
| 1636 |
+
"term": "nps",
|
| 1637 |
+
"category": "hard_skill",
|
| 1638 |
+
"source": "inferred_plausible",
|
| 1639 |
+
"confidence": "medium",
|
| 1640 |
+
"usage_guidance": "use_carefully",
|
| 1641 |
+
"example_bullet": ""
|
| 1642 |
+
},
|
| 1643 |
+
{
|
| 1644 |
+
"term": "dashboards",
|
| 1645 |
+
"category": "hard_skill",
|
| 1646 |
+
"source": "resume_original",
|
| 1647 |
+
"confidence": "high",
|
| 1648 |
+
"usage_guidance": "safe_to_use",
|
| 1649 |
+
"example_bullet": ""
|
| 1650 |
+
},
|
| 1651 |
+
{
|
| 1652 |
+
"term": "a/b tests",
|
| 1653 |
+
"category": "hard_skill",
|
| 1654 |
+
"source": "resume_original",
|
| 1655 |
+
"confidence": "high",
|
| 1656 |
+
"usage_guidance": "safe_to_use",
|
| 1657 |
+
"example_bullet": ""
|
| 1658 |
+
},
|
| 1659 |
+
{
|
| 1660 |
+
"term": "looker metabase",
|
| 1661 |
+
"category": "hard_skill",
|
| 1662 |
+
"source": "inferred_plausible",
|
| 1663 |
+
"confidence": "medium",
|
| 1664 |
+
"usage_guidance": "use_carefully",
|
| 1665 |
+
"example_bullet": ""
|
| 1666 |
+
},
|
| 1667 |
+
{
|
| 1668 |
+
"term": "amplitude",
|
| 1669 |
+
"category": "tool",
|
| 1670 |
+
"source": "inferred_plausible",
|
| 1671 |
+
"confidence": "medium",
|
| 1672 |
+
"usage_guidance": "use_carefully",
|
| 1673 |
+
"example_bullet": ""
|
| 1674 |
+
},
|
| 1675 |
+
{
|
| 1676 |
+
"term": "mixpanel",
|
| 1677 |
+
"category": "tool",
|
| 1678 |
+
"source": "inferred_plausible",
|
| 1679 |
+
"confidence": "medium",
|
| 1680 |
+
"usage_guidance": "use_carefully",
|
| 1681 |
+
"example_bullet": ""
|
| 1682 |
+
},
|
| 1683 |
+
{
|
| 1684 |
+
"term": "b2c",
|
| 1685 |
+
"category": "domain",
|
| 1686 |
+
"source": "inferred_plausible",
|
| 1687 |
+
"confidence": "medium",
|
| 1688 |
+
"usage_guidance": "use_carefully",
|
| 1689 |
+
"example_bullet": ""
|
| 1690 |
+
},
|
| 1691 |
+
{
|
| 1692 |
+
"term": "marketplace",
|
| 1693 |
+
"category": "domain",
|
| 1694 |
+
"source": "inferred_plausible",
|
| 1695 |
+
"confidence": "medium",
|
| 1696 |
+
"usage_guidance": "use_carefully",
|
| 1697 |
+
"example_bullet": ""
|
| 1698 |
+
},
|
| 1699 |
+
{
|
| 1700 |
+
"term": "program",
|
| 1701 |
+
"category": "hard_skill",
|
| 1702 |
+
"source": "inferred_plausible",
|
| 1703 |
+
"confidence": "medium",
|
| 1704 |
+
"usage_guidance": "use_carefully",
|
| 1705 |
+
"example_bullet": ""
|
| 1706 |
+
},
|
| 1707 |
+
{
|
| 1708 |
+
"term": "product discovery",
|
| 1709 |
+
"category": "responsibility",
|
| 1710 |
+
"source": "resume_original",
|
| 1711 |
+
"confidence": "high",
|
| 1712 |
+
"usage_guidance": "safe_to_use",
|
| 1713 |
+
"example_bullet": ""
|
| 1714 |
+
},
|
| 1715 |
+
{
|
| 1716 |
+
"term": "user research",
|
| 1717 |
+
"category": "responsibility",
|
| 1718 |
+
"source": "resume_original",
|
| 1719 |
+
"confidence": "high",
|
| 1720 |
+
"usage_guidance": "safe_to_use",
|
| 1721 |
+
"example_bullet": ""
|
| 1722 |
+
},
|
| 1723 |
+
{
|
| 1724 |
+
"term": "a/b testing",
|
| 1725 |
+
"category": "responsibility",
|
| 1726 |
+
"source": "resume_original",
|
| 1727 |
+
"confidence": "high",
|
| 1728 |
+
"usage_guidance": "safe_to_use",
|
| 1729 |
+
"example_bullet": ""
|
| 1730 |
+
}
|
| 1731 |
+
]
|
| 1732 |
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"_cache_key": "v2_1781861629562490500_94919",
|
| 3 |
+
"resume": {
|
| 4 |
+
"name": "Saiteja Tirunagari",
|
| 5 |
+
"contact": {
|
| 6 |
+
"phone": "+91 9988996588",
|
| 7 |
+
"email": "saitejatirunagari@gmail.com",
|
| 8 |
+
"linkedin": "https://linkedin.com/in/saitejatirunagari",
|
| 9 |
+
"website": "",
|
| 10 |
+
"location": "AI-First Product Manager · 5+ Years · Hyderabad, India"
|
| 11 |
+
},
|
| 12 |
+
"summary": "AI-first Product Manager with 5+ years driving 0→1 product development in fast-paced EdTech startups. Proven record building digital acquisition funnels, automation pipelines, and AI-powered tools that have processed 140,000+ users and contributed to 2× revenue growth. Deep expertise in conversational AI, OCR automation, funnel optimization, A/B experimentation, and cross- functional delivery. Combines data-driven decision-making with user-centric design to ship measurable outcomes—from +35 pp payment conversion lifts to 3.8× report engagement jumps.",
|
| 13 |
+
"skills": [],
|
| 14 |
+
"roles": [
|
| 15 |
+
{
|
| 16 |
+
"title": "Internal Product Manager",
|
| 17 |
+
"company": "NxtWave Disruptive Technologies Pvt. Ltd.",
|
| 18 |
+
"location": "Hyderabad, India",
|
| 19 |
+
"dates": "Jan 2023 – Present",
|
| 20 |
+
"bullets": [
|
| 21 |
+
"Led end-to-end revamp of NIAT Application Portal—a unified digital funnel covering landing pages → OTP login → personal details → payment → slot booking → exam → report → sales flow—integrated with CRM, WebEngage, and payment systems",
|
| 22 |
+
"Scaled to 141,269 OTP-verified leads; achieved 97% personal-details completion and 95% exam-attendance rate across 23,983 attendees, with 210 final enrollments",
|
| 23 |
+
"Maintained ₹1,120+ Cr annual pipeline value across 8,000+ processed applications",
|
| 24 |
+
"Introduced contextual loaders and UX refinements, eliminating idle wait perception and reducing early-stage drop-offs",
|
| 25 |
+
"Implemented coupon-based urgency logic in the payment flow, lifting 0–60 min payment completion from 27.37% → 63.24% (+35.87 pp) for coupon users",
|
| 26 |
+
"Improved overall funnel payment conversion by +6.98 pp, accelerating time-to-revenue with near-zero acquisition cost increase",
|
| 27 |
+
"Redesigned the NIAT landing-page AI chatbot into a structured conversion engine with stage-wise decision trees and CRM- integrated nudges aligned to every funnel milestone (lead → application → payment → exam → enrollment)",
|
| 28 |
+
"Generated 6,776 leads and 113 enrollments via chatbot-driven funnel; chatbot independently sourced 1,744 leads and 440 applications"
|
| 29 |
+
]
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"title": "Asst. Product Success Manager – User Experience",
|
| 33 |
+
"company": "Think & Learn Pvt. Ltd. (BYJU'S)",
|
| 34 |
+
"location": "Bengaluru, India",
|
| 35 |
+
"dates": "Oct 2021 – Dec 2022",
|
| 36 |
+
"bullets": [
|
| 37 |
+
"Managed 20 customer-success specialists covering 40,000 customers; maintained refund rate below 5% and customer satisfaction above 95%",
|
| 38 |
+
"Played 0→1 role in Xplore Experiment and Social Emotional Learning pilot projects alongside product and engineering teams",
|
| 39 |
+
"Sustained 95%+ Monthly Recurring Revenue from existing EMI customers through proactive retention strategies",
|
| 40 |
+
"Designed robust processes and drove adherence across teams to ensure consistent execution and sustainable growth",
|
| 41 |
+
"Created comprehensive customer documentation and educated users on new product capabilities and technical feasibility"
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"title": "Product Specialist – User Experience",
|
| 46 |
+
"company": "Think & Learn Pvt. Ltd. (BYJU'S)",
|
| 47 |
+
"location": "Bengaluru, India",
|
| 48 |
+
"dates": "Aug 2019 – Sep 2021",
|
| 49 |
+
"bullets": [
|
| 50 |
+
"Increased user retention by 8% by redesigning the onboarding process using UX research and user-centric principles",
|
| 51 |
+
"Conducted extensive UX research and A/B testing to identify pain points and refine features, improving learning-platform engagement",
|
| 52 |
+
"Mentored students throughout their academic journey using multi-channel communication; monitored performance dashboards and shared progress reports with stakeholders"
|
| 53 |
+
]
|
| 54 |
+
},
|
| 55 |
+
{
|
| 56 |
+
"title": "Founder & CEO",
|
| 57 |
+
"company": "ML Edutech",
|
| 58 |
+
"location": "Hyderabad, India",
|
| 59 |
+
"dates": "Aug 2015 – Jul 2019",
|
| 60 |
+
"bullets": [
|
| 61 |
+
"Launched EdTech app portfolio of 275 apps with 3 million+ cumulative downloads",
|
| 62 |
+
"Drove user acquisition through Google Ads, LinkedIn, and paid social; established strategic partnerships and managed end-to- end P&L",
|
| 63 |
+
"Built a performance-driven culture focused on conversion optimization, data-driven decision-making, and sustainable growth"
|
| 64 |
+
]
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"achievements": [
|
| 68 |
+
"141,269 OTP-verified leads processed through rebuilt NIAT Application Portal (2026 cycle)",
|
| 69 |
+
"2× business revenue growth in <9 months via automation and AI-powered funnel optimization",
|
| 70 |
+
"Payment conversion: 27.37% → 63.24% (+35.87 pp) for coupon users · Overall lift +6.98 pp",
|
| 71 |
+
"₹1,120+ Cr annual pipeline managed across 8,000+ applications",
|
| 72 |
+
"8,000+ admissions applications processed; ₹45,600 offline exam revenue from 1,503 paid users"
|
| 73 |
+
],
|
| 74 |
+
"education": [
|
| 75 |
+
{
|
| 76 |
+
"degree": "Diploma – Product & Brand Management",
|
| 77 |
+
"institution": "IIM Rohtak",
|
| 78 |
+
"dates": "Mar 2023 – Sep 2023"
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"degree": "Diploma in Business Management",
|
| 82 |
+
"institution": "Osmania University, Hyderabad",
|
| 83 |
+
"dates": "Aug 2015 – Jul 2019"
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"degree": "Bachelor of Engineering – Civil Engineering",
|
| 87 |
+
"institution": "JNTU Hyderabad",
|
| 88 |
+
"dates": "Aug 2011 – Sep 2016"
|
| 89 |
+
}
|
| 90 |
+
]
|
| 91 |
+
}
|
| 92 |
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""LIVE V1 acceptance harness — real model, real JDs, real résumé, real PDFs.
|
| 2 |
+
|
| 3 |
+
Runs the PRODUCTION path (nim_fallback.build_llm -> generate_alignment_safe with
|
| 4 |
+
run_audit) against 3 real job postings + the real candidate résumé, renders the
|
| 5 |
+
real ATS-safe PDF, parses it with two independent parsers (pdftotext + pymupdf),
|
| 6 |
+
and runs the independent post-generation audit. No mocks, no hard-coded criteria.
|
| 7 |
+
|
| 8 |
+
Run: python scripts/accept_v1_live.py
|
| 9 |
+
Outputs PDFs + a JSON evidence file under data/acceptance/out/.
|
| 10 |
+
"""
|
| 11 |
+
import json
|
| 12 |
+
import os
|
| 13 |
+
import re
|
| 14 |
+
import sys
|
| 15 |
+
|
| 16 |
+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
| 17 |
+
|
| 18 |
+
from src.default_resume import get_default_resume_latex
|
| 19 |
+
from src.nim_fallback import build_llm
|
| 20 |
+
from src.ats_safe import generate_alignment_safe, to_legacy_report
|
| 21 |
+
from src.latex_resume import latex_to_text
|
| 22 |
+
|
| 23 |
+
ACC = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
| 24 |
+
"data", "acceptance")
|
| 25 |
+
OUT = os.path.join(ACC, "out")
|
| 26 |
+
os.makedirs(OUT, exist_ok=True)
|
| 27 |
+
|
| 28 |
+
CASES = [
|
| 29 |
+
("case1_stripe_payments", "Staff Product Manager, Payments", "jd1_stripe_payments.txt"),
|
| 30 |
+
("case2_stripe_genai", "Staff Product Manager, ML & GenAI", "jd2_stripe_genai.txt"),
|
| 31 |
+
("case3_growth_marketing", "Growth Marketing Manager, B2B SaaS", "jd3_growth_marketing.txt"),
|
| 32 |
+
]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def _load(fn):
|
| 36 |
+
raw = open(os.path.join(ACC, fn), encoding="utf-8").read()
|
| 37 |
+
url = ""
|
| 38 |
+
m = re.search(r"SOURCE_URL:\s*(\S+)", raw)
|
| 39 |
+
if m:
|
| 40 |
+
url = m.group(1)
|
| 41 |
+
raw = raw[m.end():].strip()
|
| 42 |
+
return url, raw
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def run_case(slug, title, fn, llm, health):
|
| 46 |
+
url, jd = _load(fn)
|
| 47 |
+
resume = get_default_resume_latex()
|
| 48 |
+
safe = generate_alignment_safe(
|
| 49 |
+
resume, jd, company="", job_title=title,
|
| 50 |
+
llm_client=llm, selected_model=getattr(llm, "model", None),
|
| 51 |
+
model_health=health, run_audit=True, out_dir=OUT, compile_pdf=True)
|
| 52 |
+
est = safe.get("internal_alignment_estimate") or {}
|
| 53 |
+
audit = safe.get("independent_audit") or {}
|
| 54 |
+
accept = safe.get("acceptance") or {}
|
| 55 |
+
twop = safe.get("pdf_two_parser") or {}
|
| 56 |
+
|
| 57 |
+
# persist PDF under a stable name
|
| 58 |
+
pdf_out = os.path.join(OUT, f"{slug}.pdf")
|
| 59 |
+
if safe.get("pdf_path") and os.path.exists(safe["pdf_path"]) and safe["pdf_path"] != pdf_out:
|
| 60 |
+
import shutil
|
| 61 |
+
shutil.copy(safe["pdf_path"], pdf_out)
|
| 62 |
+
|
| 63 |
+
evidence = {
|
| 64 |
+
"case": slug, "title": title, "source_url": url,
|
| 65 |
+
"selected_model": getattr(llm, "model", None),
|
| 66 |
+
"status": safe.get("status"), "reason": safe.get("reason"),
|
| 67 |
+
"jd_clean_confidence": safe.get("jd_diagnostics", {}).get("confidence"),
|
| 68 |
+
"calibration": safe.get("calibration"),
|
| 69 |
+
"applied_rewrites": [{"phrase": r["exact_jd_phrase"], "before": r["original_resume_text"],
|
| 70 |
+
"after": r["rewritten_text"], "type": r["change_type"]}
|
| 71 |
+
for r in safe.get("rewrites", []) if r.get("applied")],
|
| 72 |
+
"rejected_rewrites": [{"phrase": r["exact_jd_phrase"], "reason": r["reject_reason"]}
|
| 73 |
+
for r in safe.get("rewrites", []) if not r.get("applied") and r.get("reject_reason")],
|
| 74 |
+
"summary_rewrite": safe.get("summary_rewrite"),
|
| 75 |
+
"gaps": [g["keyword"] for g in safe.get("evidence", {}).get("gaps", [])],
|
| 76 |
+
"score_before": est.get("before"), "score_after": est.get("after"),
|
| 77 |
+
"max_evidence_supported": est.get("max_evidence_supported"),
|
| 78 |
+
"gate_90_passed": est.get("gate_90_passed"),
|
| 79 |
+
"scored_from": est.get("scored_from"),
|
| 80 |
+
"independent_audit": audit,
|
| 81 |
+
"pdf_two_parser": twop,
|
| 82 |
+
"acceptance": accept,
|
| 83 |
+
"pdf_path": pdf_out if os.path.exists(pdf_out) else safe.get("pdf_path"),
|
| 84 |
+
}
|
| 85 |
+
with open(os.path.join(OUT, f"{slug}.json"), "w", encoding="utf-8") as f:
|
| 86 |
+
json.dump(evidence, f, indent=1, ensure_ascii=False)
|
| 87 |
+
return evidence
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def main():
|
| 91 |
+
llm, health = build_llm(timeout=30)
|
| 92 |
+
print("SELECTED MODEL:", getattr(llm, "model", None))
|
| 93 |
+
for h in health:
|
| 94 |
+
print(f" {h['requested_model']:<42} {h['status']}")
|
| 95 |
+
if llm is None:
|
| 96 |
+
print("live_model_unavailable — cannot run live acceptance.")
|
| 97 |
+
return
|
| 98 |
+
summary = []
|
| 99 |
+
for slug, title, fn in CASES:
|
| 100 |
+
print(f"\n===== {slug} : {title} =====")
|
| 101 |
+
ev = run_case(slug, title, fn, llm, health)
|
| 102 |
+
print(f" model={ev['selected_model']} status={ev['status']}")
|
| 103 |
+
print(f" before={ev['score_before']} after={ev['score_after']} "
|
| 104 |
+
f"ceiling={ev['max_evidence_supported']} gate90={ev['gate_90_passed']}")
|
| 105 |
+
print(f" applied={len(ev['applied_rewrites'])} gaps={len(ev['gaps'])} "
|
| 106 |
+
f"summary_applied={(ev['summary_rewrite'] or {}).get('applied')}")
|
| 107 |
+
au = ev["independent_audit"]
|
| 108 |
+
print(f" AUDIT: indep_score={au.get('independent_score')} "
|
| 109 |
+
f"mand_missing={au.get('supported_mandatory_missing')} "
|
| 110 |
+
f"crit_missing={au.get('supported_critical_missing')}")
|
| 111 |
+
print(f" 2-parser: present_in_both={ev['pdf_two_parser'].get('present_in_both')}"
|
| 112 |
+
f"/{ev['pdf_two_parser'].get('keywords_checked')} "
|
| 113 |
+
f"missing={ev['pdf_two_parser'].get('missing_or_split')}")
|
| 114 |
+
print(f" ACCEPTANCE: {ev['acceptance']}")
|
| 115 |
+
print(f" PDF: {ev['pdf_path']}")
|
| 116 |
+
summary.append(ev)
|
| 117 |
+
with open(os.path.join(OUT, "SUMMARY.json"), "w", encoding="utf-8") as f:
|
| 118 |
+
json.dump(summary, f, indent=1, ensure_ascii=False)
|
| 119 |
+
print("\n==== DONE. Evidence in data/acceptance/out/ ====")
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
if __name__ == "__main__":
|
| 123 |
+
main()
|
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Independent post-generation keyword-completeness audit.
|
| 2 |
+
|
| 3 |
+
Runs a SECOND, independent extraction from the cleaned JD (a fresh model call,
|
| 4 |
+
not the generator's criterion list) and compares it against the FINAL PDF-parsed
|
| 5 |
+
text. This stops the system from awarding itself a high score when its first
|
| 6 |
+
extractor missed important JD terms — the audit, not the generator, has the last
|
| 7 |
+
word on the reported score.
|
| 8 |
+
"""
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
from typing import Dict, List
|
| 12 |
+
|
| 13 |
+
from .keyword_schema import validate_and_repair, calibrate
|
| 14 |
+
from .evidence_gate import map_evidence
|
| 15 |
+
from .ats_score import score_alignment, compute_coverage, _present
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def independent_audit(clean_jd: str, original_resume_text: str,
|
| 19 |
+
final_pdf_text: str, llm) -> Dict:
|
| 20 |
+
"""Return an independent audit of the final résumé against the JD.
|
| 21 |
+
|
| 22 |
+
`llm` performs a fresh extraction (independent of the generator). Evidence is
|
| 23 |
+
judged against the ORIGINAL résumé (what the candidate genuinely supports);
|
| 24 |
+
presence is judged against the FINAL PDF-parsed text (what an ATS will read).
|
| 25 |
+
"""
|
| 26 |
+
audit: Dict = {"ok": False, "reason": "", "independent_criteria": 0}
|
| 27 |
+
try:
|
| 28 |
+
raw = llm.extract_keywords_structured(clean_jd)
|
| 29 |
+
except Exception as e:
|
| 30 |
+
audit["reason"] = f"independent_extraction_failed:{str(e)[:120]}"
|
| 31 |
+
return audit
|
| 32 |
+
valid, _ = validate_and_repair(raw, clean_jd)
|
| 33 |
+
if not valid:
|
| 34 |
+
audit["reason"] = "independent_extraction_empty"
|
| 35 |
+
return audit
|
| 36 |
+
valid = calibrate(valid)
|
| 37 |
+
audit["independent_criteria"] = len(valid)
|
| 38 |
+
|
| 39 |
+
# supported = evidence in the ORIGINAL résumé
|
| 40 |
+
ev = map_evidence(valid, original_resume_text)
|
| 41 |
+
supported = {c.keyword for c in ev.covered}
|
| 42 |
+
low_final = (final_pdf_text or "").lower()
|
| 43 |
+
|
| 44 |
+
def _in_final(c):
|
| 45 |
+
return (_present(low_final, c.get("exact_phrase", ""))
|
| 46 |
+
or _present(low_final, c.get("normalized_concept", "")))
|
| 47 |
+
|
| 48 |
+
mand = [c for c in valid if c.get("requirement_type") == "required"]
|
| 49 |
+
crit = [c for c in valid if (c.get("calibration_weight") or 0) > 0]
|
| 50 |
+
|
| 51 |
+
def _split(items):
|
| 52 |
+
sup = [c for c in items if c.get("normalized_concept") in supported]
|
| 53 |
+
return ([c["exact_phrase"] for c in sup if _in_final(c)],
|
| 54 |
+
[c["exact_phrase"] for c in sup if not _in_final(c)])
|
| 55 |
+
|
| 56 |
+
mand_present, mand_missing = _split(mand)
|
| 57 |
+
crit_present, crit_missing = _split(crit)
|
| 58 |
+
# exact-phrase presence over supported critical
|
| 59 |
+
sup_crit = [c for c in crit if c.get("normalized_concept") in supported]
|
| 60 |
+
exact_present = [c["exact_phrase"] for c in sup_crit
|
| 61 |
+
if _present(low_final, c.get("exact_phrase", ""))]
|
| 62 |
+
exact_missing = [c["exact_phrase"] for c in sup_crit
|
| 63 |
+
if not _present(low_final, c.get("exact_phrase", ""))]
|
| 64 |
+
# concepts covered only semantically (partial evidence, not a clear capability)
|
| 65 |
+
semantic_only = [p.exact_phrase for p in ev.partial]
|
| 66 |
+
# unsupported requirements correctly excluded (gaps not in final)
|
| 67 |
+
unsupported_excluded = [g.keyword for g in ev.gaps
|
| 68 |
+
if not _present(low_final, g.keyword)]
|
| 69 |
+
unsupported_leaked = [g.keyword for g in ev.gaps
|
| 70 |
+
if _present(low_final, g.keyword)]
|
| 71 |
+
|
| 72 |
+
# Score from FINAL PDF text using the INDEPENDENT criteria.
|
| 73 |
+
score = score_alignment(valid, ev.to_dict(), final_pdf_text)
|
| 74 |
+
cov = score["coverage"]
|
| 75 |
+
|
| 76 |
+
audit.update({
|
| 77 |
+
"ok": True, "reason": "audited",
|
| 78 |
+
"supported_mandatory_present": mand_present,
|
| 79 |
+
"supported_mandatory_missing": mand_missing,
|
| 80 |
+
"supported_critical_present": crit_present,
|
| 81 |
+
"supported_critical_missing": crit_missing,
|
| 82 |
+
"important_exact_present": exact_present,
|
| 83 |
+
"important_exact_missing": exact_missing,
|
| 84 |
+
"semantic_only": semantic_only,
|
| 85 |
+
"unsupported_correctly_excluded": unsupported_excluded,
|
| 86 |
+
"unsupported_leaked_into_resume": unsupported_leaked,
|
| 87 |
+
"coverage": cov,
|
| 88 |
+
"independent_score": score["score"],
|
| 89 |
+
"gate_90_passed": score["gate_90_passed"],
|
| 90 |
+
})
|
| 91 |
+
return audit
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def acceptance_verdict(audit: Dict, unsupported_insertions: int,
|
| 95 |
+
stuffing_penalty: float, pdf_two_parser: Dict) -> Dict:
|
| 96 |
+
"""Final acceptance decision. A score >=90 is only ACCEPTED when the
|
| 97 |
+
independent audit + PDF checks all pass; otherwise the reported score is
|
| 98 |
+
capped and the reasons are listed."""
|
| 99 |
+
reasons = []
|
| 100 |
+
if not audit.get("ok"):
|
| 101 |
+
return {"accepted_score": 0.0, "accepted": False,
|
| 102 |
+
"reasons": [audit.get("reason", "audit_failed")]}
|
| 103 |
+
cov = audit.get("coverage", {})
|
| 104 |
+
if audit.get("supported_mandatory_missing"):
|
| 105 |
+
reasons.append("supported mandatory criteria missing from final PDF")
|
| 106 |
+
if (cov.get("critical_family_coverage") or 0) < 0.90:
|
| 107 |
+
reasons.append("critical family coverage < 90%")
|
| 108 |
+
if (cov.get("critical_exact_phrase_coverage") or 0) < 0.85:
|
| 109 |
+
reasons.append("critical exact-phrase coverage < 85%")
|
| 110 |
+
if unsupported_insertions > 0:
|
| 111 |
+
reasons.append("unsupported insertions present")
|
| 112 |
+
if audit.get("unsupported_leaked_into_resume"):
|
| 113 |
+
reasons.append("unsupported requirement leaked into résumé")
|
| 114 |
+
if stuffing_penalty > 0:
|
| 115 |
+
reasons.append("keyword-stuffing penalty > 0")
|
| 116 |
+
if pdf_two_parser and pdf_two_parser.get("missing_or_split"):
|
| 117 |
+
reasons.append("accepted keyword not present in both parsers")
|
| 118 |
+
score = audit.get("independent_score", 0.0)
|
| 119 |
+
accepted_90 = (score >= 90) and not reasons
|
| 120 |
+
if score >= 90 and reasons:
|
| 121 |
+
score = min(score, 89.0) # cannot claim >=90 unless audit clean
|
| 122 |
+
return {"accepted_score": score, "accepted_ge_90": accepted_90,
|
| 123 |
+
"blocking_reasons": reasons}
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__": # ponytail: light self-check with a deterministic stub
|
| 127 |
+
class Stub:
|
| 128 |
+
def extract_keywords_structured(self, jd):
|
| 129 |
+
return [{"exact_phrase": "SQL", "normalized_concept": "sql", "category": "tool",
|
| 130 |
+
"requirement_type": "required", "importance": "high", "source_text": "SQL",
|
| 131 |
+
"semantic_variants": [], "confidence": 0.9, "requires_resume_evidence": True}]
|
| 132 |
+
jd = "Requirements: you must have strong SQL."
|
| 133 |
+
a = independent_audit(jd, "Built SQL dashboards.", "Built SQL dashboards for analytics.", Stub())
|
| 134 |
+
assert a["ok"] and "SQL" in a["supported_mandatory_present"], a
|
| 135 |
+
print("ats_evaluate self-check PASSED:", a["independent_score"], a["gate_90_passed"])
|
|
@@ -21,6 +21,7 @@ Greenhouse score.
|
|
| 21 |
"""
|
| 22 |
from __future__ import annotations
|
| 23 |
|
|
|
|
| 24 |
import re
|
| 25 |
import tempfile
|
| 26 |
from typing import Dict, List, Optional
|
|
@@ -69,15 +70,19 @@ def _deterministic_report_only_items(clean_jd: str) -> List[dict]:
|
|
| 69 |
|
| 70 |
|
| 71 |
def _resume_section_order(latex_src: str, resume_text: str) -> List[str]:
|
| 72 |
-
"""
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
| 76 |
for c in candidates:
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
|
| 83 |
def generate_alignment_safe(
|
|
@@ -88,8 +93,10 @@ def generate_alignment_safe(
|
|
| 88 |
job_title: str = "",
|
| 89 |
llm_client=None,
|
| 90 |
rewrite_fn=None,
|
|
|
|
| 91 |
selected_model: Optional[str] = None,
|
| 92 |
model_health: Optional[list] = None,
|
|
|
|
| 93 |
out_dir: Optional[str] = None,
|
| 94 |
compile_pdf: bool = True,
|
| 95 |
progress_callback=None,
|
|
@@ -165,6 +172,23 @@ def generate_alignment_safe(
|
|
| 165 |
raw_items = _deterministic_report_only_items(clean_jd)
|
| 166 |
report["extraction"]["used_fallback"] = used_fallback
|
| 167 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
# 3. Validate + traceability gate.
|
| 169 |
_prog("Validating extraction…", 40)
|
| 170 |
valid, rejected = validate_and_repair(raw_items, clean_jd)
|
|
@@ -210,6 +234,25 @@ def generate_alignment_safe(
|
|
| 210 |
latex_src, ev_before.rewrite_candidates(), effective_rewrite_fn)
|
| 211 |
rewrite_records = [r.to_dict() for r in recs]
|
| 212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
# 6. Compile + PDF-parse (so scoring can use PARSED text, not just LaTeX).
|
| 214 |
_compile_preserved(report, final_latex, out_dir, job_title, compile_pdf,
|
| 215 |
compile_latex_to_pdf, _safe_jobname, _prog,
|
|
@@ -276,6 +319,26 @@ def generate_alignment_safe(
|
|
| 276 |
"mandatory_recall": ev_after.metrics().get("mandatory_recall"),
|
| 277 |
"scored_from": "parsed_pdf" if report.get("_pdf_text_used") else "latex_text",
|
| 278 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
return report
|
| 280 |
|
| 281 |
|
|
@@ -423,7 +486,21 @@ def _compile_preserved(report, latex_src, out_dir, job_title, compile_pdf,
|
|
| 423 |
report["compile_log"] = comp.get("log", "")
|
| 424 |
except Exception as e:
|
| 425 |
report["compile_log"] = f"compile_error: {e}"
|
| 426 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
|
| 428 |
if report.get("pdf_path"):
|
| 429 |
from .pdf_validate import validate_pdf
|
|
|
|
| 21 |
"""
|
| 22 |
from __future__ import annotations
|
| 23 |
|
| 24 |
+
import os
|
| 25 |
import re
|
| 26 |
import tempfile
|
| 27 |
from typing import Dict, List, Optional
|
|
|
|
| 70 |
|
| 71 |
|
| 72 |
def _resume_section_order(latex_src: str, resume_text: str) -> List[str]:
|
| 73 |
+
"""Actual section order as it appears in the résumé SOURCE (so PDF-order
|
| 74 |
+
validation compares against the résumé's own order, not a fixed assumption)."""
|
| 75 |
+
candidates = ["summary", "experience", "projects", "education", "skills",
|
| 76 |
+
"certifications"]
|
| 77 |
+
found = []
|
| 78 |
for c in candidates:
|
| 79 |
+
m = re.search(r"\\section\*?\{[^}]*" + re.escape(c) + r"[^}]*\}", latex_src, re.I)
|
| 80 |
+
pos = m.start() if m else (latex_src.lower().find(c.upper().lower())
|
| 81 |
+
if c.upper() in latex_src else -1)
|
| 82 |
+
if pos >= 0:
|
| 83 |
+
found.append((pos, c))
|
| 84 |
+
ordered = [c for _, c in sorted(found)]
|
| 85 |
+
return ordered or ["experience", "education", "skills"]
|
| 86 |
|
| 87 |
|
| 88 |
def generate_alignment_safe(
|
|
|
|
| 93 |
job_title: str = "",
|
| 94 |
llm_client=None,
|
| 95 |
rewrite_fn=None,
|
| 96 |
+
summary_fn=None,
|
| 97 |
selected_model: Optional[str] = None,
|
| 98 |
model_health: Optional[list] = None,
|
| 99 |
+
run_audit: bool = False,
|
| 100 |
out_dir: Optional[str] = None,
|
| 101 |
compile_pdf: bool = True,
|
| 102 |
progress_callback=None,
|
|
|
|
| 172 |
raw_items = _deterministic_report_only_items(clean_jd)
|
| 173 |
report["extraction"]["used_fallback"] = used_fallback
|
| 174 |
|
| 175 |
+
# 2.5. Completeness check — if the live extraction missed obvious source-
|
| 176 |
+
# grounded requirements (named tools, explicit years), retry ONCE with a
|
| 177 |
+
# correction hint before trusting it. (No award from an incomplete list.)
|
| 178 |
+
if not used_fallback and llm_client is not None:
|
| 179 |
+
try:
|
| 180 |
+
from .nim_fallback import extraction_gaps
|
| 181 |
+
_pre_valid, _ = validate_and_repair(raw_items, clean_jd)
|
| 182 |
+
gaps = extraction_gaps(clean_jd, calibrate(_pre_valid))
|
| 183 |
+
if gaps:
|
| 184 |
+
report["extraction"]["completeness_retry"] = gaps
|
| 185 |
+
retried = llm_client.extract_keywords_structured(
|
| 186 |
+
clean_jd, correction_hint=", ".join(gaps))
|
| 187 |
+
if retried:
|
| 188 |
+
raw_items = retried
|
| 189 |
+
except Exception as e:
|
| 190 |
+
report["extraction"]["completeness_error"] = str(e)[:120]
|
| 191 |
+
|
| 192 |
# 3. Validate + traceability gate.
|
| 193 |
_prog("Validating extraction…", 40)
|
| 194 |
valid, rejected = validate_and_repair(raw_items, clean_jd)
|
|
|
|
| 234 |
latex_src, ev_before.rewrite_candidates(), effective_rewrite_fn)
|
| 235 |
rewrite_records = [r.to_dict() for r in recs]
|
| 236 |
|
| 237 |
+
# 5.5. Headline/summary optimization — rewrite the SUMMARY toward the target
|
| 238 |
+
# role using ONLY résumé-corpus facts + top supported phrases (corpus-
|
| 239 |
+
# verified; keeps original on any fabrication signal).
|
| 240 |
+
eff_summary_fn = summary_fn
|
| 241 |
+
if eff_summary_fn is None and llm_client is not None \
|
| 242 |
+
and hasattr(llm_client, "rewrite_summary"):
|
| 243 |
+
eff_summary_fn = llm_client.rewrite_summary
|
| 244 |
+
report["summary_rewrite"] = None
|
| 245 |
+
if eff_summary_fn is not None:
|
| 246 |
+
from .resume_rewrite import optimize_summary
|
| 247 |
+
top_phrases = [c["exact_phrase"] for c in valid
|
| 248 |
+
if (c.get("calibration_weight") or 0) > 0
|
| 249 |
+
and any(cc.keyword == c["normalized_concept"]
|
| 250 |
+
for cc in ev_before.covered)][:6]
|
| 251 |
+
new_latex, srec = optimize_summary(
|
| 252 |
+
final_latex, job_title or "", top_phrases, resume_text, eff_summary_fn)
|
| 253 |
+
final_latex = new_latex
|
| 254 |
+
report["summary_rewrite"] = srec
|
| 255 |
+
|
| 256 |
# 6. Compile + PDF-parse (so scoring can use PARSED text, not just LaTeX).
|
| 257 |
_compile_preserved(report, final_latex, out_dir, job_title, compile_pdf,
|
| 258 |
compile_latex_to_pdf, _safe_jobname, _prog,
|
|
|
|
| 319 |
"mandatory_recall": ev_after.metrics().get("mandatory_recall"),
|
| 320 |
"scored_from": "parsed_pdf" if report.get("_pdf_text_used") else "latex_text",
|
| 321 |
}
|
| 322 |
+
|
| 323 |
+
# 8. INDEPENDENT audit + two-parser PDF verification (acceptance gate). The
|
| 324 |
+
# audit re-extracts from the JD independently and scores from the PARSED
|
| 325 |
+
# PDF; the reported acceptance score cannot exceed what the audit confirms.
|
| 326 |
+
if run_audit and llm_client is not None and report.get("_pdf_text_used"):
|
| 327 |
+
try:
|
| 328 |
+
from .ats_evaluate import independent_audit, acceptance_verdict
|
| 329 |
+
from .pdf_validate import _extract_pdf_text, verify_keywords_two_parsers
|
| 330 |
+
pdf_text = _extract_pdf_text(report["pdf_path"]) or score_text
|
| 331 |
+
audit = independent_audit(clean_jd, resume_text, pdf_text, llm_client)
|
| 332 |
+
accepted_kw = [r.get("exact_jd_phrase") for r in rewrite_records
|
| 333 |
+
if r.get("applied")]
|
| 334 |
+
two_parser = verify_keywords_two_parsers(report["pdf_path"], accepted_kw) \
|
| 335 |
+
if accepted_kw else {}
|
| 336 |
+
verdict = acceptance_verdict(audit, 0, stuffing, two_parser)
|
| 337 |
+
report["independent_audit"] = audit
|
| 338 |
+
report["pdf_two_parser"] = two_parser
|
| 339 |
+
report["acceptance"] = verdict
|
| 340 |
+
except Exception as e:
|
| 341 |
+
report["audit_error"] = str(e)[:160]
|
| 342 |
return report
|
| 343 |
|
| 344 |
|
|
|
|
| 486 |
report["compile_log"] = comp.get("log", "")
|
| 487 |
except Exception as e:
|
| 488 |
report["compile_log"] = f"compile_error: {e}"
|
| 489 |
+
|
| 490 |
+
# ATS-safe fallback: when no LaTeX engine compiled a PDF, render the résumé as
|
| 491 |
+
# a single-column plain-text PDF (reportlab). This is what an ATS reads anyway,
|
| 492 |
+
# and it guarantees a real, parseable PDF on any host (incl. no-tectonic).
|
| 493 |
+
if not report.get("pdf_path"):
|
| 494 |
+
try:
|
| 495 |
+
from .latex_resume import latex_to_text, render_text_to_pdf
|
| 496 |
+
fb = os.path.join(out_dir, f"{jobname}.pdf")
|
| 497 |
+
if render_text_to_pdf(latex_to_text(latex_src), fb) and os.path.exists(fb):
|
| 498 |
+
report["pdf_path"] = fb
|
| 499 |
+
report["engine"] = report.get("engine") or "reportlab-atsafe"
|
| 500 |
+
report["compiled"] = True
|
| 501 |
+
report["pdf_fallback"] = True
|
| 502 |
+
except Exception as e:
|
| 503 |
+
report["compile_log"] = (report.get("compile_log", "") + f" | fallback: {e}")
|
| 504 |
|
| 505 |
if report.get("pdf_path"):
|
| 506 |
from .pdf_validate import validate_pdf
|
|
@@ -130,7 +130,13 @@ def score_alignment(criteria: List[dict], evidence: Dict, final_text: str,
|
|
| 130 |
s_ss = s_ss if s_ss is not None else 1.0
|
| 131 |
|
| 132 |
pv = pdf_validation or {}
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
|
| 135 |
comp = {
|
| 136 |
"mandatory": s_mand, "critical": s_crit, "exact_phrase": s_exact,
|
|
@@ -155,7 +161,7 @@ def score_alignment(criteria: List[dict], evidence: Dict, final_text: str,
|
|
| 155 |
and (cov["critical_family_coverage"] or 0) >= GATE_90["critical_family_coverage"]
|
| 156 |
and (cov["critical_exact_phrase_coverage"] or 0) >= GATE_90["critical_exact_phrase_coverage"]
|
| 157 |
and stuffing_penalty == 0
|
| 158 |
-
and (not pv or pv.get("
|
| 159 |
)
|
| 160 |
if score >= 90 and not gate_ok:
|
| 161 |
score = 89.0
|
|
|
|
| 130 |
s_ss = s_ss if s_ss is not None else 1.0
|
| 131 |
|
| 132 |
pv = pdf_validation or {}
|
| 133 |
+
# Parsing quality hinges on: text recovered + no hidden/injected markers.
|
| 134 |
+
# (Exact section-heading naming is a diagnostic, not a scoring blocker.)
|
| 135 |
+
if pv:
|
| 136 |
+
_parse_clean = pv.get("parser_recovered_text") and not pv.get("forbidden_markers_found")
|
| 137 |
+
s_parse = 1.0 if _parse_clean else (0.5 if pv.get("parser_recovered_text") else 0.0)
|
| 138 |
+
else:
|
| 139 |
+
s_parse = 1.0
|
| 140 |
|
| 141 |
comp = {
|
| 142 |
"mandatory": s_mand, "critical": s_crit, "exact_phrase": s_exact,
|
|
|
|
| 161 |
and (cov["critical_family_coverage"] or 0) >= GATE_90["critical_family_coverage"]
|
| 162 |
and (cov["critical_exact_phrase_coverage"] or 0) >= GATE_90["critical_exact_phrase_coverage"]
|
| 163 |
and stuffing_penalty == 0
|
| 164 |
+
and (not pv or (pv.get("parser_recovered_text") and not pv.get("forbidden_markers_found")))
|
| 165 |
)
|
| 166 |
if score >= 90 and not gate_ok:
|
| 167 |
score = 89.0
|
|
@@ -76,7 +76,7 @@ class LLMClient:
|
|
| 76 |
# ──────────────────────────────────────────────────────────
|
| 77 |
# STRUCTURED KEYWORD EXTRACTION — injection-resistant, schema'd
|
| 78 |
# ──────────────────────────────────────────────────────────
|
| 79 |
-
def extract_keywords_structured(self, clean_jd: str) -> list[dict]:
|
| 80 |
"""Extract structured, traceable hiring criteria from an ALREADY-CLEANED JD.
|
| 81 |
|
| 82 |
The JD text is treated strictly as untrusted DATA delimited by fences. The
|
|
@@ -118,10 +118,14 @@ class LLMClient:
|
|
| 118 |
"marketing copy, and generic adjectives.\n"
|
| 119 |
"- Prefer 15-25 high-value criteria over a long weak list."
|
| 120 |
)
|
|
|
|
|
|
|
|
|
|
| 121 |
user = (
|
| 122 |
"<<<JD_START>>>\n"
|
| 123 |
f"{(clean_jd or '')[:6000]}\n"
|
| 124 |
-
"<<<JD_END>>>\n
|
|
|
|
| 125 |
"Return the JSON array now."
|
| 126 |
)
|
| 127 |
try:
|
|
@@ -174,6 +178,35 @@ class LLMClient:
|
|
| 174 |
print(f"[rewrite_bullet] failed: {e}")
|
| 175 |
return original_bullet
|
| 176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
# ──────────────────────────────────────────────────────────
|
| 178 |
# KEYWORD EXTRACTION — Calibrated Keyword Match Framework (legacy flat list)
|
| 179 |
# ──────────────────────────────────────────────────────────
|
|
|
|
| 76 |
# ──────────────────────────────────────────────────────────
|
| 77 |
# STRUCTURED KEYWORD EXTRACTION — injection-resistant, schema'd
|
| 78 |
# ──────────────────────────────────────────────────────────
|
| 79 |
+
def extract_keywords_structured(self, clean_jd: str, correction_hint: str = "") -> list[dict]:
|
| 80 |
"""Extract structured, traceable hiring criteria from an ALREADY-CLEANED JD.
|
| 81 |
|
| 82 |
The JD text is treated strictly as untrusted DATA delimited by fences. The
|
|
|
|
| 118 |
"marketing copy, and generic adjectives.\n"
|
| 119 |
"- Prefer 15-25 high-value criteria over a long weak list."
|
| 120 |
)
|
| 121 |
+
hint = ("\n\nIMPORTANT: your previous extraction MISSED these source-grounded "
|
| 122 |
+
f"requirements — include them if present verbatim: {correction_hint}"
|
| 123 |
+
if correction_hint else "")
|
| 124 |
user = (
|
| 125 |
"<<<JD_START>>>\n"
|
| 126 |
f"{(clean_jd or '')[:6000]}\n"
|
| 127 |
+
"<<<JD_END>>>\n"
|
| 128 |
+
f"{hint}\n"
|
| 129 |
"Return the JSON array now."
|
| 130 |
)
|
| 131 |
try:
|
|
|
|
| 178 |
print(f"[rewrite_bullet] failed: {e}")
|
| 179 |
return original_bullet
|
| 180 |
|
| 181 |
+
def rewrite_summary(self, original_summary: str, target_title: str,
|
| 182 |
+
top_phrases: list, resume_corpus: str) -> str:
|
| 183 |
+
"""Rewrite the résumé SUMMARY to target the role, using ONLY facts already
|
| 184 |
+
in the résumé. Output re-verified against the corpus by the caller."""
|
| 185 |
+
system = (
|
| 186 |
+
"You rewrite a résumé professional-summary paragraph to target a "
|
| 187 |
+
"specific role, staying strictly truthful.\n"
|
| 188 |
+
"HARD RULES:\n"
|
| 189 |
+
"- Use ONLY facts, skills, metrics, and experience already present in "
|
| 190 |
+
"the RÉSUMÉ CORPUS. Do not add any new employer, tool, metric, number, "
|
| 191 |
+
"industry, title, or claim not already in the corpus.\n"
|
| 192 |
+
"- Naturally incorporate the TARGET PHRASES only where the corpus "
|
| 193 |
+
"genuinely supports them; skip any that would be untrue.\n"
|
| 194 |
+
"- Keep it 2-4 sentences, natural and recruiter-readable. Not a keyword list.\n"
|
| 195 |
+
"- Return ONLY the rewritten summary text."
|
| 196 |
+
)
|
| 197 |
+
user = (
|
| 198 |
+
f"TARGET TITLE: {target_title}\n"
|
| 199 |
+
f"TARGET PHRASES (use only if truthful): {', '.join(top_phrases)}\n\n"
|
| 200 |
+
f"RÉSUMÉ CORPUS (the only allowed source of facts):\n{resume_corpus[:3500]}\n\n"
|
| 201 |
+
f"ORIGINAL SUMMARY:\n{original_summary}\n\n"
|
| 202 |
+
"Rewritten summary:"
|
| 203 |
+
)
|
| 204 |
+
try:
|
| 205 |
+
return (self._call(system, user, max_tokens=500) or "").strip().strip('"')
|
| 206 |
+
except Exception as e:
|
| 207 |
+
print(f"[rewrite_summary] failed: {e}")
|
| 208 |
+
return original_summary
|
| 209 |
+
|
| 210 |
# ──────────────────────────────────────────────────────────
|
| 211 |
# KEYWORD EXTRACTION — Calibrated Keyword Match Framework (legacy flat list)
|
| 212 |
# ──────────────────────────────────────────────────────────
|
|
@@ -109,6 +109,32 @@ def reset_cache() -> None:
|
|
| 109 |
_CACHE["logs"] = []
|
| 110 |
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
def select_model(chain: Optional[List[str]] = None,
|
| 113 |
timeout: float = 25.0, use_cache: bool = True) -> Tuple[Optional[str], List[dict]]:
|
| 114 |
"""Return (first_healthy_model | None, per-model health logs)."""
|
|
|
|
| 109 |
_CACHE["logs"] = []
|
| 110 |
|
| 111 |
|
| 112 |
+
def extraction_gaps(clean_jd: str, valid_items: list) -> list:
|
| 113 |
+
"""Deterministic completeness check: obvious source-grounded requirements the
|
| 114 |
+
extraction should have captured but didn't. Returns a list of missed phrases
|
| 115 |
+
(named tools present in the JD, an explicit years-of-experience requirement).
|
| 116 |
+
Used to trigger a correction retry / failover before trusting an extraction."""
|
| 117 |
+
import re
|
| 118 |
+
try:
|
| 119 |
+
from .jd_analyzer import _TOOLS
|
| 120 |
+
except Exception:
|
| 121 |
+
_TOOLS = set()
|
| 122 |
+
low = (clean_jd or "").lower()
|
| 123 |
+
got = " ".join((v.get("exact_phrase", "") + " " + v.get("normalized_concept", ""))
|
| 124 |
+
for v in (valid_items or [])).lower()
|
| 125 |
+
missed = []
|
| 126 |
+
# named tools/technologies present verbatim in the JD but absent from criteria
|
| 127 |
+
for tool in _TOOLS:
|
| 128 |
+
if re.search(r"(?<![a-z0-9])" + re.escape(tool) + r"(?![a-z0-9])", low) \
|
| 129 |
+
and tool not in got:
|
| 130 |
+
missed.append(tool)
|
| 131 |
+
# explicit years-of-experience requirement (e.g. "10+ years")
|
| 132 |
+
ym = re.search(r"\b(\d{1,2}\+?\s*years?)\b", low)
|
| 133 |
+
if ym and "year" not in got:
|
| 134 |
+
missed.append(ym.group(1))
|
| 135 |
+
return missed[:12]
|
| 136 |
+
|
| 137 |
+
|
| 138 |
def select_model(chain: Optional[List[str]] = None,
|
| 139 |
timeout: float = 25.0, use_cache: bool = True) -> Tuple[Optional[str], List[dict]]:
|
| 140 |
"""Return (first_healthy_model | None, per-model health logs)."""
|
|
@@ -34,6 +34,64 @@ def _extract_pdf_text(pdf_path: str) -> Optional[str]:
|
|
| 34 |
return None
|
| 35 |
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
def validate_pdf(
|
| 38 |
pdf_path: str,
|
| 39 |
*,
|
|
@@ -67,10 +125,13 @@ def validate_pdf(
|
|
| 67 |
expected_sections = expected_sections or ["experience", "education", "skills"]
|
| 68 |
|
| 69 |
def _heading_pos(sec: str) -> int:
|
|
|
|
| 70 |
for m in re.finditer(r"(?im)^[^\S\n]*([A-Za-z &/]{3,40})[^\S\n]*$", text):
|
| 71 |
line = m.group(1).strip().lower()
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
| 74 |
return m.start()
|
| 75 |
return -1
|
| 76 |
|
|
|
|
| 34 |
return None
|
| 35 |
|
| 36 |
|
| 37 |
+
def _pdftotext_extract(pdf_path: str) -> Optional[str]:
|
| 38 |
+
"""Independent parser #2: the poppler `pdftotext` CLI. None if unavailable."""
|
| 39 |
+
import shutil
|
| 40 |
+
import subprocess
|
| 41 |
+
exe = shutil.which("pdftotext")
|
| 42 |
+
if not exe:
|
| 43 |
+
return None
|
| 44 |
+
try:
|
| 45 |
+
out = subprocess.run([exe, "-layout", pdf_path, "-"],
|
| 46 |
+
capture_output=True, timeout=30)
|
| 47 |
+
txt = out.stdout.decode("utf-8", errors="replace")
|
| 48 |
+
return txt if txt.strip() else None
|
| 49 |
+
except Exception:
|
| 50 |
+
return None
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def verify_keywords_two_parsers(pdf_path: str, keywords: List[str]) -> Dict:
|
| 54 |
+
"""Confirm each accepted keyword survives BOTH independent parsers (a Python
|
| 55 |
+
lib + poppler's pdftotext). Flags any keyword split/corrupted/missing in
|
| 56 |
+
either. Falls back to a second Python parser (pymupdf) when pdftotext is
|
| 57 |
+
absent, so there are always two independent extractions."""
|
| 58 |
+
import re as _re
|
| 59 |
+
py = _extract_pdf_text(pdf_path) or ""
|
| 60 |
+
cli = _pdftotext_extract(pdf_path)
|
| 61 |
+
parser2_name = "pdftotext"
|
| 62 |
+
if cli is None:
|
| 63 |
+
# fall back to a genuinely different Python engine
|
| 64 |
+
try:
|
| 65 |
+
import fitz
|
| 66 |
+
doc = fitz.open(pdf_path)
|
| 67 |
+
cli = "\n".join(p.get_text() for p in doc)
|
| 68 |
+
doc.close()
|
| 69 |
+
parser2_name = "pymupdf"
|
| 70 |
+
except Exception:
|
| 71 |
+
cli = ""
|
| 72 |
+
def _present(text, kw):
|
| 73 |
+
p = _re.sub(r"\s+", " ", (kw or "").lower()).strip()
|
| 74 |
+
toks = [_re.escape(t) for t in p.split()]
|
| 75 |
+
if not toks:
|
| 76 |
+
return False
|
| 77 |
+
pat = r"(?<![a-z0-9])" + r"[\s\W]{0,3}".join(toks) + r"(?![a-z0-9])"
|
| 78 |
+
return _re.search(pat, text.lower()) is not None
|
| 79 |
+
results = []
|
| 80 |
+
for kw in keywords:
|
| 81 |
+
in_py, in_cli = _present(py, kw), _present(cli, kw)
|
| 82 |
+
results.append({"keyword": kw, "parser1_pdfplumber": in_py,
|
| 83 |
+
f"parser2_{parser2_name}": in_cli,
|
| 84 |
+
"in_both": in_py and in_cli})
|
| 85 |
+
return {
|
| 86 |
+
"parser1": "pdfplumber/pymupdf", "parser2": parser2_name,
|
| 87 |
+
"parser1_chars": len(py), "parser2_chars": len(cli),
|
| 88 |
+
"keywords_checked": len(keywords),
|
| 89 |
+
"present_in_both": sum(1 for r in results if r["in_both"]),
|
| 90 |
+
"missing_or_split": [r["keyword"] for r in results if not r["in_both"]],
|
| 91 |
+
"per_keyword": results,
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
def validate_pdf(
|
| 96 |
pdf_path: str,
|
| 97 |
*,
|
|
|
|
| 125 |
expected_sections = expected_sections or ["experience", "education", "skills"]
|
| 126 |
|
| 127 |
def _heading_pos(sec: str) -> int:
|
| 128 |
+
s = sec.lower()
|
| 129 |
for m in re.finditer(r"(?im)^[^\S\n]*([A-Za-z &/]{3,40})[^\S\n]*$", text):
|
| 130 |
line = m.group(1).strip().lower()
|
| 131 |
+
# near-exact heading line only (résumé headings stand alone) — never a
|
| 132 |
+
# prose line that merely starts with the section word.
|
| 133 |
+
if line == s or line.rstrip("s") == s.rstrip("s") \
|
| 134 |
+
or (line.startswith(s) and len(line) <= len(s) + 3):
|
| 135 |
return m.start()
|
| 136 |
return -1
|
| 137 |
|
|
@@ -341,6 +341,91 @@ def _rebuild_bullet(b: dict, new_text: str, latex_escape) -> str:
|
|
| 341 |
return r"\item " + esc
|
| 342 |
|
| 343 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 344 |
# ── Reference (deterministic) rewriter — demo/offline stand-in for the LLM ──────
|
| 345 |
|
| 346 |
def reference_rewrite_fn(original: str, target_phrase: str,
|
|
|
|
| 341 |
return r"\item " + esc
|
| 342 |
|
| 343 |
|
| 344 |
+
# ── Headline / summary optimization (corpus-verified prose rewriting) ───────────
|
| 345 |
+
|
| 346 |
+
def verify_against_corpus(new_text: str, corpus_text: str,
|
| 347 |
+
target_phrases) -> Tuple[bool, str]:
|
| 348 |
+
"""Fabrication guard for prose (summary/headline). The rewrite may reuse ANY
|
| 349 |
+
fact already in the résumé corpus + the target JD phrases + connective words,
|
| 350 |
+
but must introduce NO new number and NO new content token beyond those."""
|
| 351 |
+
phrases = [target_phrases] if isinstance(target_phrases, str) else list(target_phrases or [])
|
| 352 |
+
if not (new_text or "").strip():
|
| 353 |
+
return False, "empty"
|
| 354 |
+
if _KEYWORD_LIST_RE.search(new_text):
|
| 355 |
+
return False, "keyword_list_pattern"
|
| 356 |
+
if not _nums(new_text) <= _nums(corpus_text):
|
| 357 |
+
return False, "new_metric_introduced"
|
| 358 |
+
corpus_tok = {t.lower() for t in _content_tokens(corpus_text)}
|
| 359 |
+
target_tok = {t.lower() for ph in phrases for t in _content_tokens(ph)}
|
| 360 |
+
for t in _content_tokens(new_text):
|
| 361 |
+
tl = t.lower()
|
| 362 |
+
if tl in corpus_tok or tl in target_tok or tl in _ALLOWED_NEW:
|
| 363 |
+
continue
|
| 364 |
+
if any(tl.startswith(c[:4]) and abs(len(tl) - len(c)) <= 3
|
| 365 |
+
for c in corpus_tok if len(c) >= 4):
|
| 366 |
+
continue
|
| 367 |
+
return False, f"new_content_token:{tl}"
|
| 368 |
+
return True, "ok"
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def locate_summary(latex_src: str):
|
| 372 |
+
"""Return (start, end, inner) of the SUMMARY paragraph's \\small{...} content,
|
| 373 |
+
or None. Matches the résumé's `\\section{SUMMARY} ... \\small{ <inner> }`."""
|
| 374 |
+
m = re.search(r"\\section\*?\{\s*(?:summary|professional summary|profile)\s*\}",
|
| 375 |
+
latex_src, re.I)
|
| 376 |
+
if not m:
|
| 377 |
+
return None
|
| 378 |
+
k = latex_src.find(r"\small{", m.end())
|
| 379 |
+
if k < 0 or k - m.end() > 400:
|
| 380 |
+
return None
|
| 381 |
+
open_brace = k + len(r"\small")
|
| 382 |
+
depth, i = 0, open_brace
|
| 383 |
+
while i < len(latex_src):
|
| 384 |
+
if latex_src[i] == "{":
|
| 385 |
+
depth += 1
|
| 386 |
+
elif latex_src[i] == "}":
|
| 387 |
+
depth -= 1
|
| 388 |
+
if depth == 0:
|
| 389 |
+
return (open_brace + 1, i, latex_src[open_brace + 1:i])
|
| 390 |
+
i += 1
|
| 391 |
+
return None
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
def optimize_summary(latex_src: str, target_title: str, top_phrases: List[str],
|
| 395 |
+
corpus_text: str, summary_fn) -> Tuple[str, dict]:
|
| 396 |
+
"""Rewrite the SUMMARY paragraph via `summary_fn`, verified against the résumé
|
| 397 |
+
corpus. `summary_fn(original, target_title, top_phrases, corpus) -> str`.
|
| 398 |
+
Returns (new_latex, record). Keeps original on any verification failure."""
|
| 399 |
+
from .latex_resume import latex_to_text, latex_escape
|
| 400 |
+
loc = locate_summary(latex_src)
|
| 401 |
+
rec = {"kind": "summary", "applied": False, "reject_reason": "",
|
| 402 |
+
"original": "", "rewritten": "", "change_type": "no_change_required"}
|
| 403 |
+
if not loc:
|
| 404 |
+
rec["reject_reason"] = "no_summary_section"
|
| 405 |
+
return latex_src, rec
|
| 406 |
+
s, e, inner = loc
|
| 407 |
+
original_plain = re.sub(r"\s+", " ", latex_to_text(inner)).strip()
|
| 408 |
+
rec["original"] = original_plain
|
| 409 |
+
try:
|
| 410 |
+
proposed = summary_fn(original_plain, target_title, top_phrases, corpus_text)
|
| 411 |
+
except Exception as ex:
|
| 412 |
+
rec["reject_reason"] = f"summary_fn_error:{str(ex)[:60]}"
|
| 413 |
+
return latex_src, rec
|
| 414 |
+
proposed = re.sub(r"\s+", " ", (proposed or "")).strip()
|
| 415 |
+
rec["rewritten"] = proposed
|
| 416 |
+
if not proposed or proposed == original_plain:
|
| 417 |
+
rec["reject_reason"] = "no_change"
|
| 418 |
+
return latex_src, rec
|
| 419 |
+
ok, why = verify_against_corpus(proposed, corpus_text, top_phrases)
|
| 420 |
+
if not ok:
|
| 421 |
+
rec["reject_reason"] = why
|
| 422 |
+
return latex_src, rec
|
| 423 |
+
rec["applied"] = True
|
| 424 |
+
rec["change_type"] = "summary_optimization"
|
| 425 |
+
new_latex = latex_src[:s] + latex_escape(proposed) + latex_src[e:]
|
| 426 |
+
return new_latex, rec
|
| 427 |
+
|
| 428 |
+
|
| 429 |
# ── Reference (deterministic) rewriter — demo/offline stand-in for the LLM ──────
|
| 430 |
|
| 431 |
def reference_rewrite_fn(original: str, target_phrase: str,
|