Refocus resume as Principal Security Engineer; tighten to 2 pages

- Reposition summary and skills toward hands-on AI + automation (IC track);
  lead skills with AI/ML Engineering, demote leadership
- Condense summary to lead with a hard metric (47% of 3,500 weekly tickets)
- Fold NWTF role into Earlier Experience to fit 2 pages
- Add build_resume.sh: regenerate docx, render PDF via OnlyOffice x2t,
  and enforce a hard 3-page maximum
- Fix timezone off-by-one in work dates and cert/award years

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-06 11:19:56 -05:00
parent 57d3796785
commit ff3adfc438
5 changed files with 134 additions and 87 deletions

View File

@@ -1,7 +1,7 @@
const fs = require('fs');
const {
Document, Packer, Paragraph, TextRun, AlignmentType,
HeadingLevel, LevelFormat, ExternalHyperlink, BorderStyle
HeadingLevel, LevelFormat, ExternalHyperlink, BorderStyle, PageBreak
} = require('docx');
// ============================================
@@ -21,7 +21,7 @@ const resumeData = JSON.parse(fs.readFileSync(INPUT_FILE, 'utf8'));
function createBulletParagraph(text, reference) {
return new Paragraph({
numbering: { reference: reference, level: 0 },
spacing: { after: 60 },
spacing: { after: 40 },
children: [new TextRun({ text: text, size: 22, font: "Arial" })]
});
}
@@ -29,7 +29,9 @@ function createBulletParagraph(text, reference) {
// Format date from YYYY-MM to "Mon YYYY"
function formatDate(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr + '-01');
// Build from explicit parts so the date is local-time (avoids UTC-parse month shift)
const [year, month] = dateStr.split('-').map(Number);
const date = new Date(year, (month || 1) - 1, 1);
return date.toLocaleDateString('en-US', { month: 'short', year: 'numeric' });
}
@@ -109,7 +111,7 @@ children.push(new Paragraph({
// --- CAREER EXPERIENCE SECTION ---
children.push(new Paragraph({
spacing: { before: 120, after: 200 },
spacing: { before: 80, after: 120 },
children: [new TextRun({
text: "Career Experience",
bold: true,
@@ -128,7 +130,7 @@ resumeData.work.forEach((job, index) => {
// Job title and company
children.push(new Paragraph({
spacing: { before: 160, after: 60 },
spacing: { before: 120, after: 40 },
children: [
new TextRun({
text: job.position,
@@ -153,7 +155,7 @@ resumeData.work.forEach((job, index) => {
// Job summary
if (job.summary) {
children.push(new Paragraph({
spacing: { after: 100 },
spacing: { after: 60 },
children: [new TextRun({
text: job.summary,
size: 22,
@@ -170,12 +172,12 @@ resumeData.work.forEach((job, index) => {
}
// Spacing after each job
children.push(new Paragraph({ spacing: { after: 120 }, children: [] }));
children.push(new Paragraph({ spacing: { after: 60 }, children: [] }));
});
// --- EDUCATION, CERTIFICATIONS, AND AWARDS SECTION ---
children.push(new Paragraph({
spacing: { before: 200, after: 160 },
spacing: { before: 120, after: 100 },
children: [new TextRun({
text: "Education, Certifications, and Awards",
bold: true,
@@ -187,7 +189,7 @@ children.push(new Paragraph({
// Certificates
if (resumeData.certificates) {
resumeData.certificates.forEach(cert => {
const year = cert.date ? new Date(cert.date).getFullYear() : '';
const year = cert.date ? cert.date.slice(0, 4) : '';
children.push(new Paragraph({
spacing: { after: 60 },
children: [
@@ -215,7 +217,7 @@ if (resumeData.certificates) {
// Awards
if (resumeData.awards) {
resumeData.awards.forEach(award => {
const year = award.date ? new Date(award.date).getFullYear() : '';
const year = award.date ? award.date.slice(0, 4) : '';
children.push(new Paragraph({
spacing: { after: 60 },
children: [
@@ -294,7 +296,7 @@ if (resumeData.education) {
// --- TECHNICAL SKILLS SECTION ---
children.push(new Paragraph({
spacing: { before: 200, after: 160 },
spacing: { before: 120, after: 100 },
children: [new TextRun({
text: "Technical Skills",
bold: true,