Revamp: fix invisible hero title on Steam Deck, tighten copy sitewide, clean junk files, move to OpenVibers
Changes
ddeleted file mode 100755@@@ -1,299 +0,0 @@--from reportlab.pdfbase import pdfmetrics--from reportlab.pdfbase.ttfonts import TTFont--from reportlab.lib.pagesizes import letter--from reportlab.lib.colors import HexColor, white--from reportlab.pdfgen import canvas--from reportlab.platypus import Paragraph--from reportlab.lib.styles import ParagraphStyle----pdfmetrics.registerFont(TTFont('FA', 'fontawesome-webfont.ttf'))----PAGE_W, PAGE_H = letter----# Palette--BG = HexColor('#eef2f7')--BLUE = HexColor('#4f84d6')--BLUE_DARK = HexColor('#2a4f90')--BLUE_LINE = HexColor('#7ea8ee')--BLUE_SOFT = HexColor('#5f92de')--TEXT = HexColor('#34548f')--SHADOW = HexColor('#d9dee8')--CARD_BG = HexColor('#f8fbff')----# Typography--BODY = ParagraphStyle('Body', fontName='Helvetica', fontSize=10.4, leading=12.1, textColor=TEXT, spaceAfter=0)--COMPANY = ParagraphStyle('Company', fontName='Helvetica-Bold', fontSize=9.8, leading=11, textColor=BLUE)--BULLET = ParagraphStyle('Bullet', fontName='Helvetica', fontSize=9.15, leading=10.25, textColor=TEXT)--CARD_TITLE = ParagraphStyle('CardTitle', fontName='Helvetica-Bold', fontSize=10.9, leading=12.2, textColor=BLUE_DARK)--CARD_DESC = ParagraphStyle('CardDesc', fontName='Helvetica', fontSize=8.35, leading=9.15, textColor=TEXT)--PRODUCT_TITLE = ParagraphStyle('ProductTitle', fontName='Helvetica-Bold', fontSize=10.2, leading=11.4, textColor=BLUE_DARK)--URL_STYLE = ParagraphStyle('URL', fontName='Helvetica', fontSize=8.7, leading=9.4, textColor=BLUE)--LEARN = ParagraphStyle('Learn', fontName='Helvetica', fontSize=8.7, leading=9.8, textColor=TEXT)--EXP_TITLE = ParagraphStyle('ExpTitle', fontName='Helvetica-Bold', fontSize=14.6, leading=16.0, textColor=BLUE_DARK)----products = [-- ('\uf0ac','HoboTools','hobo.tools','Shared-account ecosystem and network hub','https://hobo.tools'),-- ('\uf03d','HoboStreamer','hobostreamer.com','Streaming, clips, VODs, chat, creator tools','https://hobostreamer.com'),-- ('\uf041','HoboMaps','maps.hobo.tools','Map overlays, research, and survival data','https://maps.hobo.tools'),-- ('\uf11b','HoboQuest','hobo.quest','Browser MMORPG, multiplayer games, live canvas','https://hobo.quest'),--]----core_cards = [-- ('\uf201','Analytics / Data','SQL, Excel, reporting, dashboards, defect research, integrity, and root-cause analysis'),-- ('\uf121','Engineering / Web','JavaScript, TypeScript, Node.js, Python, PHP, React, Vue, Electron, HTML5, CSS/SASS, REST APIs'),-- ('\uf1c0','Systems / Infrastructure','Linux, Windows Server, NGINX, Apache, MongoDB, Redis, Cloudflare, deployment, scaling, and DDoS resilience'),-- ('\uf11b','Game Dev / Product','Lua, C#, C++, gameplay systems, creator tooling, UX, graphics, and product thinking'),--]------def round_rect(c, x, y, w, h, r, fill, stroke=BLUE_LINE, stroke_width=1, shadow=True, shadow_dx=4, shadow_dy=-4):-- if shadow:-- c.setFillColor(SHADOW)-- c.setStrokeColor(SHADOW)-- c.roundRect(x+shadow_dx, y+shadow_dy, w, h, r, fill=1, stroke=0)-- c.setFillColor(fill)-- c.setStrokeColor(stroke)-- c.setLineWidth(stroke_width)-- c.roundRect(x, y, w, h, r, fill=1, stroke=1)------def para(c, html, style, x, top, width, height=200):-- p = Paragraph(html, style)-- _, h = p.wrap(width, height)-- p.drawOn(c, x, top-h)-- return h------def draw_icon_circle(c, x, y, size, glyph, fg=BLUE, bg=CARD_BG, font_size=15):-- c.setFillColor(bg)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(1.1)-- c.circle(x + size/2, y + size/2, size/2, fill=1, stroke=1)-- c.setFont('FA', font_size)-- c.setFillColor(fg)-- c.drawCentredString(x + size/2, y + size/2 - font_size*0.34, glyph)------def section_header(c, x, y, w, text, glyph):-- round_rect(c, x, y, w, 30, 15, BLUE, stroke=BLUE, stroke_width=0.8, shadow=True)-- draw_icon_circle(c, x+10, y+4, 22, glyph, fg=white, bg=BLUE_SOFT, font_size=12)-- c.setFont('Helvetica-Bold', 15)-- c.setFillColor(white)-- c.drawString(x+38, y+9, text)------def chip_width(text):-- return pdfmetrics.stringWidth(text, 'Helvetica-Bold', 8.9) + 28------def draw_chip(c, x, y, text, glyph, h=22):-- w = chip_width(text)-- c.setFillColor(BLUE_SOFT)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.8)-- c.roundRect(x, y, w, h, h/2, fill=1, stroke=1)-- c.setFont('FA', 10)-- c.setFillColor(white)-- c.drawString(x+9, y+6, glyph)-- c.setFont('Helvetica-Bold', 8.9)-- c.drawString(x+22, y+7, text)-- return w------def draw_header(c, x, y, w, h):-- round_rect(c, x, y, w, h, 28, BLUE, stroke=BLUE, stroke_width=0.8, shadow=True)-- c.setFillColor(white)-- c.setFont('Helvetica-Bold', 31)-- c.drawString(x+20, y+h-46, 'Alex Frison')---- subtitle_style = ParagraphStyle('subtitle', fontName='Helvetica-Bold', fontSize=10.3, leading=11.0, textColor=white)-- subtitle = ('Data analyst and systems-minded builder across analytics, robotics support, retail sales leadership, and full-stack platforms.')-- para(c, subtitle, subtitle_style, x+22, y+h-74, 330, 40)---- # Chips - always inside the header; fixed 2-row layout-- row1 = [('\uf201','Analytics / BI'),('\uf121','Full Stack Eng'),('\uf0ad','Robotics / RME')]-- row2 = [('\uf07a','Sales Leadership'),('\uf0b1','Product Builder')]-- cx = x + 22-- for glyph, text in row1:-- cx += draw_chip(c, cx, y+42, text, glyph) + 10-- cx = x + 22-- for glyph, text in row2:-- cx += draw_chip(c, cx, y+14, text, glyph) + 10---- # contact panel-- cp_w, cp_h = 206, 84-- cp_x, cp_y = x + w - cp_w - 18, y + h - cp_h - 18-- round_rect(c, cp_x, cp_y, cp_w, cp_h, 18, BLUE_SOFT, stroke=BLUE_LINE, stroke_width=0.9, shadow=False)-- top_icon_y = cp_y + cp_h - 36-- bottom_icon_y = cp_y + 12-- draw_icon_circle(c, cp_x+12, top_icon_y, 24, '\uf095', fg=white, bg=BLUE_SOFT, font_size=12)-- draw_icon_circle(c, cp_x+12, bottom_icon_y, 24, '\uf0e0', fg=white, bg=BLUE_SOFT, font_size=11)-- c.setStrokeColor(HexColor('#83a8e6'))-- c.setLineWidth(0.8)-- c.line(cp_x+18, cp_y + cp_h/2, cp_x+cp_w-18, cp_y + cp_h/2)-- c.setFillColor(white)-- c.setFont('Helvetica-Bold', 10.5)-- c.drawString(cp_x+42, cp_y + cp_h - 20, '+1 (425) 367-3997')-- c.drawString(cp_x+42, cp_y + 28, '[email protected]')------def top_feature_card(c, x, y, w, h, glyph, title_html, bullets, desc_size=8.45):-- round_rect(c, x, y, w, h, 18, CARD_BG, shadow=True)-- draw_icon_circle(c, x+14, y+h-46, 28, glyph, fg=BLUE, bg=CARD_BG, font_size=14)-- para(c, title_html, ParagraphStyle('ft', parent=CARD_TITLE, fontSize=11.2, leading=12.5), x+52, y+h-14, w-66, 26)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.8)-- c.line(x+18, y+h-52, x+w-18, y+h-52)-- html = '<br/>'.join(f'• {b}' for b in bullets)-- para(c, html, ParagraphStyle('fb', parent=CARD_DESC, fontSize=desc_size, leading=9.1), x+18, y+h-60, w-36, 60)------def product_card(c, x, y, w, h, glyph, title, url, desc, link):-- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)-- draw_icon_circle(c, x+12, y+h-16-22, 22, glyph, fg=BLUE, bg=CARD_BG, font_size=11)-- tx = x + 42-- para(c, title, ParagraphStyle('pt', parent=PRODUCT_TITLE, fontSize=10.1, leading=10.9), tx, y+h-10, w-(tx-x)-12, 18)-- para(c, f'<u>{url}</u>', ParagraphStyle('pu', parent=URL_STYLE, fontSize=8.6, leading=8.9), tx, y+h-30, w-(tx-x)-12, 14)-- para(c, desc, ParagraphStyle('pd', parent=CARD_DESC, fontSize=8.4, leading=8.8), tx, y+18, w-(tx-x)-12, 24)-- c.linkURL(link, (x, y, x+w, y+h), relative=0, thickness=0)------def core_card(c, x, y, w, h, glyph, title, desc):-- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)-- draw_icon_circle(c, x+12, y+h-14-22, 22, glyph, fg=BLUE, bg=CARD_BG, font_size=12)-- para(c, title, ParagraphStyle('ct2', parent=CARD_TITLE, fontSize=10.8, leading=11.4), x+42, y+h-10, w-54, 18)-- # More left margin and slightly lower top to separate from title.-- para(c, desc, ParagraphStyle('cd2', parent=CARD_DESC, fontSize=8.45, leading=9.0), x+58, y+30, w-70, 24)------def draw_experience(c, x, y_top, title, date, company, bullets, rule_gap=10):-- title_style = ParagraphStyle('et', parent=EXP_TITLE, fontSize=14.6, leading=16.0)-- title_w = PAGE_W - x - 34 - 132-- h = para(c, title, title_style, x, y_top, title_w, 24)-- c.setFont('Helvetica-Bold', 11.8)-- c.setFillColor(BLUE)-- c.drawRightString(PAGE_W-34, y_top-1, date)-- y = y_top - max(h, 14) - 7-- ch = para(c, company, COMPANY, x, y, PAGE_W-x-34, 14)-- y -= ch + 3-- bullet_html = '<br/>'.join(f'• {b}' for b in bullets)-- bh = para(c, bullet_html, BULLET, x+10, y, PAGE_W-x-44, 60)-- bottom = y - bh - rule_gap-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.8)-- c.line(x, bottom, PAGE_W-34, bottom)-- return bottom - 14------def build(path):-- c = canvas.Canvas(path, pagesize=letter)-- c.setTitle('Alex Frison Resume')---- for page in [1, 2]:-- c.setFillColor(BG)-- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)-- x = 18-- if page == 1:-- header_h = 170-- header_y = PAGE_H - 18 - header_h-- draw_header(c, x, header_y, PAGE_W-36, header_h)---- # top feature cards, tighter to header to reduce blank band-- top_y = header_y - 8 - 126-- gap = 14-- card_w = (PAGE_W - 36 - gap*3) / 4-- card_h = 126-- xs = [x, x+card_w+gap, x+(card_w+gap)*2, x+(card_w+gap)*3]-- top_feature_card(c, xs[0], top_y, card_w, card_h, '\uf201', 'Analytics /<br/>Data', [-- 'SQL, Excel, Inventory Control / Quality Assurance reporting',-- 'Defect review and discrepancy analysis',-- 'Live dashboards and analytics'-- ])-- top_feature_card(c, xs[1], top_y, card_w, card_h, '\uf0ad', 'RME /<br/>Robotics', [-- 'Amazon Robotics FC launch and automation',-- 'Robotics support and troubleshooting',-- 'Electro-mech hardware exposure'-- ])-- top_feature_card(c, xs[2], top_y, card_w, card_h, '\uf07a', 'Sales /<br/>Leadership', [-- 'Plans, devices, financing',-- 'Coaching, meetings, escalations',-- 'Retention and quota execution'-- ])-- top_feature_card(c, xs[3], top_y, card_w, card_h, '\uf03d', 'Product /<br/>Streaming', [-- 'HoboTools, HoboStreamer, HoboQuest',-- 'Clips, VODs, creator tools',-- 'Cross-service UX and operations'-- ], desc_size=8.0)---- profile_header_y = top_y - 22 - 16-- section_header(c, x, profile_header_y, PAGE_W-36, 'PROFILE', '\uf2c0')-- profile_box_y = profile_header_y - 50-- round_rect(c, x, profile_box_y-4, PAGE_W-36, 46, 18, CARD_BG, shadow=False)-- profile = ('Versatile data analyst and systems-minded builder with experience across Amazon ICQA analytics, Amazon RME support, retail sales leadership, and full-stack product development. Strong at turning operational context into clear reporting, stakeholder-ready analysis, practical tools, and process improvements.')-- para(c, profile, BODY, x+18, profile_box_y+38, PAGE_W-72, 30)---- exp_header_y = profile_box_y - 38-- section_header(c, x, exp_header_y, PAGE_W-36, 'EXPERIENCE', '\uf0b1')-- cur_y = exp_header_y - 16-- cur_y = draw_experience(c, x+18, cur_y, 'Data Analyst', '2024 - Present', 'Amazon', [-- 'Built ICQA SQL and Excel reporting for inventory accuracy, discrepancy review, and defect visibility.',-- 'Turned mismatch findings into dashboards, recurring reports, and stakeholder-ready analysis for operational decisions.'-- ])-- cur_y = draw_experience(c, x+18, cur_y, 'Reliability, Maintenance, and Engineering', '2023 - 2024', 'Amazon', [-- 'Supported launch-stage ARS robotics and conveyance operations while monitoring automation flow, floor conditions, and equipment behavior.',-- 'Assisted troubleshooting across robotics, uptime, safety, and electro-mechanical issues in a high-change launch environment.'-- ])-- draw_experience(c, x+18, cur_y, 'Assistant Manager / Sales Representative', '2020 - 2023', 'Verizon Wireless', [-- 'Sold smartphones, wireless plans, accessories, protection, financing, and credit-card offers in a quota-driven retail environment; earned district-level recognition.',-- 'Coached associates, ran monthly meetings and workshops, and improved customer service, retention, merchandising, and inventory ownership.'-- ], rule_gap=8)-- else:-- y = PAGE_H - 26-- y = draw_experience(c, x, y, 'Founder / Product Engineer / Streaming & Game Builder', 'Present', 'Hobo Tools / HoboStreamer / HoboQuest / HoboMaps', [-- 'Built shared-account web products across Hobo Tools, HoboStreamer, HoboQuest, and HoboMaps with unified UX, connected identity, and cross-service navigation.',-- 'Developed creator-facing streaming systems including clips, VODs, chat, moderation, analytics, restream support, and multi-path delivery; also built browser multiplayer game experiences and creator tools using Lua, C#, C++, gameplay systems, and product-minded UX.'-- ])-- y = draw_experience(c, x, y, 'Founder / Developer / Systems Admin', '2015 - Present', 'GameServerStats, LLC', [-- 'Built analytics-focused web platforms that track 200,000+ live game servers using JavaScript, Node.js, PHP, Python, SQL, MongoDB, Redis, HTML, and CSS.',-- 'Designed ingestion, search, dashboards, Linux infrastructure, scaling, DDoS resilience, support workflows, and business reporting.'-- ])-- y = draw_experience(c, x, y, 'Earlier Technical and Business Experience', '2012 - Present', 'Devolved, GModStore.com, eBay', [-- 'Built multiplayer game systems, digital products, graphics, support docs, e-commerce listings, moderation workflows, and monetized online communities.'-- ], rule_gap=6)---- prod_header_y = y - 16-- section_header(c, x, prod_header_y, PAGE_W-36, 'SELECTED PRODUCTS / LIVE EXAMPLES', '\uf0c1')-- pw = (PAGE_W - 36 - 18) / 2-- ph = 66-- py1 = prod_header_y - 72-- xs = [x, x+pw+18]-- product_card(c, xs[0], py1, pw, ph, *products[0])-- product_card(c, xs[1], py1, pw, ph, *products[1])-- py2 = py1 - 76-- product_card(c, xs[0], py2, pw, ph, *products[2])-- product_card(c, xs[1], py2, pw, ph, *products[3])---- core_header_y = py2 - 28-- section_header(c, x, core_header_y, PAGE_W-36, 'CORE TECHNICAL STACK', '\uf1c0')-- cw = (PAGE_W - 36 - 18) / 2-- ch = 58-- cy1 = core_header_y - 64-- core_card(c, x, cy1, cw, ch, core_cards[0][0], core_cards[0][1], core_cards[0][2])-- core_card(c, x+cw+18, cy1, cw, ch, core_cards[1][0], core_cards[1][1], core_cards[1][2])-- cy2 = cy1 - 66-- core_card(c, x, cy2, cw, ch, core_cards[2][0], core_cards[2][1], core_cards[2][2])-- core_card(c, x+cw+18, cy2, cw, ch, core_cards[3][0], core_cards[3][1], core_cards[3][2])---- learn_header_y = cy2 - 28-- section_header(c, x, learn_header_y, PAGE_W-36, 'EDUCATION / LEARNING', '\uf19d')-- learn_box_y = learn_header_y - 48-- round_rect(c, x, learn_box_y, PAGE_W-36, 40, 16, CARD_BG, shadow=False)-- learn_text = ('Snohomish High School, WA - AP Statistics, Calculus, English, Creative Writing, Spanish - Self-taught systems builder with a strong self-starter mindset, focused on analytics, infrastructure, graphics, photography, and practical problem solving.')-- para(c, learn_text, LEARN, x+16, learn_box_y+27, PAGE_W-68, 20)---- c.showPage()-- c.save()------if __name__ == '__main__':-- build('Alex_Frison_Resume.pdf')ddeleted file mode 100644@@@ -1,593 +0,0 @@--"""--build_resume_v13.py — Alex Frison resume (2026 edition).----Improvements over v12:--- Pulls the rich detail that lives on alexfrison.net (SOX/IRDR work,-- Anthropic SQL copilot, autonomous root-cause bot, OpenVibe 15-microservice-- kernel, HoboQuest custom engine, GameServerStats LLC scale, Verizon-- district-level recognition).--- New "Impact at a glance" metrics strip on page 1 (recruiter-skim friendly).--- Tighter typography, denser content, fewer wasted bands of blank space.--- Page 1 = pitch + first three roles (recent + revenue-relevant).--- Page 2 = remaining roles, six featured projects with live URLs, full-- technical stack, education + links footer.--- Same palette so it stays visually consistent with the site.----Run:-- cd /opt/repos/alexfrison.net && python build_resume_v13.py--Outputs:-- Alex_Frison_Resume.pdf (next to the script)--"""----from reportlab.pdfbase import pdfmetrics--from reportlab.pdfbase.ttfonts import TTFont--from reportlab.lib.pagesizes import letter--from reportlab.lib.colors import HexColor, white--from reportlab.pdfgen import canvas--from reportlab.platypus import Paragraph--from reportlab.lib.styles import ParagraphStyle----pdfmetrics.registerFont(TTFont('FA', 'fontawesome-webfont.ttf'))----PAGE_W, PAGE_H = letter----# --- palette ------------------------------------------------------------------BG = HexColor('#eef2f7')--BLUE = HexColor('#4f84d6')--BLUE_DARK = HexColor('#2a4f90')--BLUE_LINE = HexColor('#7ea8ee')--BLUE_SOFT = HexColor('#5f92de')--TEXT = HexColor('#34548f')--SUBTLE = HexColor('#5a73a8')--SHADOW = HexColor('#d9dee8')--CARD_BG = HexColor('#f8fbff')--ACCENT_GOLD = HexColor('#c98b1f') # used only for metric numerals----# --- styles -------------------------------------------------------------------BODY = ParagraphStyle('Body', fontName='Helvetica', fontSize=9.6, leading=11.4,-- textColor=TEXT, spaceAfter=0)--COMPANY = ParagraphStyle('Company', fontName='Helvetica-Bold', fontSize=9.6,-- leading=11, textColor=BLUE)--LOCATION = ParagraphStyle('Location', fontName='Helvetica-Oblique', fontSize=8.6,-- leading=10, textColor=SUBTLE)--BULLET = ParagraphStyle('Bullet', fontName='Helvetica', fontSize=8.6,-- leading=10.0, textColor=TEXT)--STACK = ParagraphStyle('Stack', fontName='Helvetica-Oblique', fontSize=8.2,-- leading=10, textColor=SUBTLE)--CARD_TITLE = ParagraphStyle('CardTitle', fontName='Helvetica-Bold', fontSize=10.7,-- leading=12, textColor=BLUE_DARK)--CARD_DESC = ParagraphStyle('CardDesc', fontName='Helvetica', fontSize=8.35,-- leading=9.6, textColor=TEXT)--PRODUCT_TITLE = ParagraphStyle('ProductTitle', fontName='Helvetica-Bold',-- fontSize=10.0, leading=11.2, textColor=BLUE_DARK)--URL_STYLE = ParagraphStyle('URL', fontName='Helvetica', fontSize=8.4,-- leading=9.2, textColor=BLUE)--LEARN = ParagraphStyle('Learn', fontName='Helvetica', fontSize=8.7,-- leading=10, textColor=TEXT)--EXP_TITLE = ParagraphStyle('ExpTitle', fontName='Helvetica-Bold', fontSize=13.4,-- leading=15.4, textColor=BLUE_DARK)--METRIC_NUM = ParagraphStyle('MetricNum', fontName='Helvetica-Bold', fontSize=22,-- leading=24, textColor=BLUE_DARK, alignment=1)--METRIC_LBL = ParagraphStyle('MetricLbl', fontName='Helvetica', fontSize=7.8,-- leading=9.2, textColor=TEXT, alignment=1)----# --- content -----------------------------------------------------------------PROFILE = (-- "Data analyst and systems architect with 10+ years building and shipping "-- "production systems. <b>1,000+ SOX-compliance IRDRs personally researched "-- "at Amazon ICQA</b>; built an Anthropic SQL copilot and autonomous "-- "root-cause-analysis agent in daily use. Solo architect of a "-- "<b>15-microservice open-source platform kernel</b> (OpenVibe), a "-- "live-streaming stack (HoboStreamer), and a 14-service identity network. "-- "Previously <b>district top performer → Assistant Manager at Verizon "-- "Wireless</b>, coaching 4–8 reps."--)----METRICS = [-- ('1,000+', 'SOX IRDRs hand-researched', '\uf002'),-- ('15', 'OpenVibe microservices shipped', '\uf233'),-- ('200K+', 'Live game servers tracked', '\uf11b'),-- ('20+', 'Amazon TamperMonkey/<br/>Python scripts','\uf085'),-- ('10+', 'Years running my own LLC', '\uf0b1'),--]----TOP_FEATURE_CARDS = [-- ('\uf201', 'Analytics /<br/>Data', [-- 'SQL, Power Query, Excel, dashboards',-- 'SOX-compliant IRDR research + LLM agents',-- ]),-- ('\uf085', 'Robotics /<br/>RME & AFM', [-- 'Amazon Robotics (ARS) floor monitor',-- 'Pod / drive / workstation troubleshooting',-- ]),-- ('\uf07a', 'Sales /<br/>Leadership', [-- 'Top performer; district-level recognition',-- 'Assistant Manager; coached 4–8 reps',-- ]),-- ('\uf03d', 'Product /<br/>Engineering', [-- 'OpenVibe: 15-service platform kernel',-- 'HoboStreamer, HoboQuest, Hobo Tools',-- ]),--]----EXPERIENCE = [-- {-- 'title': 'Data Analyst — ICQA',-- 'date': '2024 - 2025',-- 'company': 'Amazon · PAE2, Arlington WA',-- 'bullets': [-- 'Own SOX-compliance IRDR workflow — <b>hand-researched 1,000+ inventory mismatches</b>, authored reporting satisfying Sarbanes-Oxley audit trails.',-- 'Authored an Anthropic-powered <b>SQL copilot</b> + an <b>autonomous root-cause-analysis agent</b> that plans, gathers evidence, and writes complete IRDRs end-to-end — collapsing hours of manual research per defect into seconds.',-- ],-- 'stack': 'SQL · Excel / Power Query · Python · Anthropic API · TamperMonkey · SOX Compliance',-- },-- {-- 'title': 'Amnesty Floor Monitor (AFM) — Amazon Robotics',-- 'date': '2023 - 2024',-- 'company': 'Amazon · Robotics launch site',-- 'bullets': [-- 'Monitored Amazon Robotics (ARS) automation during launch-stage operations — enforced amnesty zones, cleared obstructions, kept pod and drive-unit flow safe.',-- 'Investigated recurring pod, drive, and workstation defects on the floor; documented patterns and escalated to RME and ops leadership to drive process improvements.',-- ],-- 'stack': 'Amazon Robotics · AFM protocols · Floor safety · Defect documentation',-- },-- {-- 'title': 'Assistant Manager / Sales Representative',-- 'date': '2020 - 2023',-- 'company': 'Verizon Wireless',-- 'bullets': [-- 'Consistently ranked among <b>top performers in the district</b> — hit and exceeded monthly quotas across smartphones, plans, accessories, device protection, financing, and Verizon Visa offers.',-- 'Promoted to Assistant Manager; mentored and coached a team of 4–8 reps through weekly 1-on-1s, floor coaching, and monthly workshops on objection handling, upselling, and customer retention. Led store opens / closes, inventory, escalations, and daily huddles.',-- ],-- 'stack': 'Consultative Sales · Team Leadership · Coaching · Retail Ops · Quota Attainment · Customer Retention',-- },-- {-- 'title': 'Founder · Systems Architect · Principal Engineer',-- 'date': '2022 - Present',-- 'company': 'Hobo Network / OpenVibe',-- 'bullets': [-- 'Sole architect and engineer on <b>OpenVibe</b> — a 15-microservice open-source platform kernel: distributed event bus, OIDC/OAuth2 identity, media object store, live ingest + restream, real-time chat, billing ledger, AI/search backbone. Self-hosted alternative to Twitch + YouTube + Patreon + Discord. 16 phases documented, 11+ shipped.',-- '<b>HoboStreamer</b>: live-streaming platform from scratch — WebRTC SFU, HLS, multi-destination RTMP restream (OBS → Twitch / YouTube / Kick), auto-recorded VODs, clip detection, chat, voice channels, analytics, moderation, and an embedded in-stream browser game. Fully open source.',-- '<b>HoboQuest</b> (browser MMORPG) + <b>hobo.tools</b> (SSO + OAuth2 across 14 services): custom isomorphic engine with Source-Engine-style client prediction, 4 live game instances; shared themes, notifications, cross-domain sessions.',-- ],-- 'stack': 'TypeScript · Node.js · Express · PostgreSQL / SQLite · Redis · WebRTC · WebSockets · OAuth2 / OIDC · FFmpeg · Lua · C#',-- },-- {-- 'title': 'Founder · Developer · Systems Admin',-- 'date': '2015 - Present',-- 'company': 'GameServerStats, LLC',-- 'bullets': [-- 'Built analytics platforms tracking <b>200,000+ live game servers</b>; #1 Google result for several major titles, licensed to third-party sites under written agreement.',-- 'Self-built and collocated 1U / 2U servers in datacenters; designed ingestion, dashboards, DDoS resilience, and ran the entire LLC — licenses, taxes, partner relations — for 10+ years.',-- ],-- 'stack': 'Node.js · PHP · Python · MongoDB · MySQL · Redis · NGINX · Linux',-- },-- {-- 'title': 'Earlier Technical & Business Experience',-- 'date': '2012 - 2015',-- 'company': 'Devolved · GModStore · eBay',-- 'bullets': [-- 'Built multiplayer game systems, in-game economies, and digital products sold through ScriptFodder / GModStore.',-- 'Created graphics, support docs, e-commerce listings, moderation workflows; ran online communities at scale.',-- ],-- 'stack': 'Lua · PHP · MySQL · Photoshop · Source Engine',-- },--]----PROJECTS = [-- ('\uf135', 'OpenVibe',-- 'github.com/HoboStreamer/OpenVibe-Temporary-MonoRepo',-- '15-microservice open-source platform kernel — identity, event bus, media store, live ingest, billing, AI/search. Sole architect across 16 phases.',-- 'https://github.com/HoboStreamer/OpenVibe-Temporary-MonoRepo'),-- ('\uf0eb', 'Autonomous IRDR Root-Cause Bot',-- 'Internal Amazon tool',-- 'Autonomous agent that plans, gathers evidence, and files SOX-compliant IRDRs end-to-end — built on 1,000+ hand-researched reports.',-- 'https://alexfrison.net/portfolio'),-- ('\uf1c0', 'Amazon SQL Copilot',-- 'Internal Amazon tool',-- 'Anthropic-powered SQL assistant that auto-discovers tables / FKs / semantics and streams runnable, table-aware SQL for ICQA analysts.',-- 'https://alexfrison.net/portfolio'),-- ('\uf03d', 'HoboStreamer',-- 'hobostreamer.com',-- 'Live streaming platform from scratch — WebRTC + HLS, multi-RTMP restream, VODs, clips, chat, voice, analytics. 100% open source.',-- 'https://hobostreamer.com'),--]----CORE_STACK = [-- ('\uf201', 'Analytics / Data',-- 'SQL · Excel + Power Query + VBA · Python (pandas) · Dashboards · ICQA reporting · Root-cause analysis · Anthropic / LLM tool-use agents'),-- ('\uf121', 'Engineering / Web',-- 'JavaScript / TypeScript · Node.js · Express · Python · PHP · React · Vue · REST APIs · HTML5 / CSS · WebSockets / WebRTC · OAuth2 / OIDC'),-- ('\uf233', 'Systems / Infrastructure',-- 'Linux · NGINX · PostgreSQL / MySQL / MongoDB / Redis · Cloudflare · DDoS resilience · Self-built 1U / 2U servers'),-- ('\uf085', 'Robotics · Game Dev · Leadership',-- 'ARS robotics support · Electro-mechanical troubleshooting · Lua / C# / C++ · Source Engine · Team coaching'),--]----LINKS = [-- ('\uf0ac', 'alexfrison.net', 'https://alexfrison.net'),-- ('\uf0e0', '[email protected]', 'mailto:[email protected]'),-- ('\uf095', '(425) 367-3997', 'tel:+14253673997'),-- ('\uf08c', 'linkedin.com/in/fris', 'https://www.linkedin.com/in/fris/'),--]------# --- primitives --------------------------------------------------------------def round_rect(c, x, y, w, h, r, fill, stroke=BLUE_LINE, stroke_width=1,-- shadow=True, shadow_dx=4, shadow_dy=-4):-- if shadow:-- c.setFillColor(SHADOW)-- c.setStrokeColor(SHADOW)-- c.roundRect(x + shadow_dx, y + shadow_dy, w, h, r, fill=1, stroke=0)-- c.setFillColor(fill)-- c.setStrokeColor(stroke)-- c.setLineWidth(stroke_width)-- c.roundRect(x, y, w, h, r, fill=1, stroke=1)------def para(c, html, style, x, top, width, height=400):-- p = Paragraph(html, style)-- _, h = p.wrap(width, height)-- p.drawOn(c, x, top - h)-- return h------def draw_icon_circle(c, x, y, size, glyph, fg=BLUE, bg=CARD_BG,-- font_size=15, stroke=BLUE_LINE):-- c.setFillColor(bg)-- c.setStrokeColor(stroke)-- c.setLineWidth(1.0)-- c.circle(x + size / 2, y + size / 2, size / 2, fill=1, stroke=1)-- c.setFont('FA', font_size)-- c.setFillColor(fg)-- c.drawCentredString(x + size / 2, y + size / 2 - font_size * 0.34, glyph)------def section_header(c, x, y, w, text, glyph, h=26):-- round_rect(c, x, y, w, h, h / 2, BLUE, stroke=BLUE, stroke_width=0.8,-- shadow=True)-- draw_icon_circle(c, x + 8, y + (h - 20) / 2, 20, glyph, fg=white,-- bg=BLUE_SOFT, font_size=11, stroke=BLUE_SOFT)-- c.setFont('Helvetica-Bold', 13)-- c.setFillColor(white)-- c.drawString(x + 36, y + (h - 13) / 2 + 1, text)------def chip_width(text):-- return pdfmetrics.stringWidth(text, 'Helvetica-Bold', 8.9) + 28------def draw_chip(c, x, y, text, glyph, h=20):-- w = chip_width(text)-- c.setFillColor(BLUE_SOFT)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.8)-- c.roundRect(x, y, w, h, h / 2, fill=1, stroke=1)-- c.setFont('FA', 10)-- c.setFillColor(white)-- c.drawString(x + 9, y + 5, glyph)-- c.setFont('Helvetica-Bold', 8.9)-- c.drawString(x + 22, y + 6, text)-- return w------def draw_header(c, x, y, w, h):-- round_rect(c, x, y, w, h, 26, BLUE, stroke=BLUE, stroke_width=0.8,-- shadow=True)-- c.setFillColor(white)-- c.setFont('Helvetica-Bold', 30)-- c.drawString(x + 22, y + h - 42, 'Alex Frison')---- role_style = ParagraphStyle('role', fontName='Helvetica-Bold',-- fontSize=10.4, leading=12.2, textColor=white)-- role = ('Data Analyst · Software Engineer · '-- 'Systems Architect')-- para(c, role, role_style, x + 24, y + h - 50, 340, 14)---- subtitle_style = ParagraphStyle('subtitle', fontName='Helvetica',-- fontSize=9.0, leading=11.0,-- textColor=white)-- subtitle = ('Versatile builder across analytics, robotics support, '-- 'retail sales leadership, and full-stack platforms. '-- 'Pacific Northwest based — open to hybrid, remote, on-site.')-- para(c, subtitle, subtitle_style, x + 24, y + h - 64, 340, 30)---- # role chips, two rows-- row1 = [('\uf201', 'Analytics / BI'),-- ('\uf121', 'Full Stack Eng'),-- ('\uf085', 'Robotics / RME')]-- row2 = [('\uf07a', 'Sales Leadership'),-- ('\uf0b1', 'Product Builder'),-- ('\uf135', 'Systems Architect')]-- cx = x + 24-- for glyph, text in row1:-- cx += draw_chip(c, cx, y + 36, text, glyph) + 8-- cx = x + 24-- for glyph, text in row2:-- cx += draw_chip(c, cx, y + 12, text, glyph) + 8---- # contact panel-- cp_w, cp_h = 212, 92-- cp_x, cp_y = x + w - cp_w - 18, y + h - cp_h - 16-- round_rect(c, cp_x, cp_y, cp_w, cp_h, 16, BLUE_SOFT,-- stroke=BLUE_LINE, stroke_width=0.9, shadow=False)---- # three rows: phone / email / web-- items = [-- ('\uf095', '+1 (425) 367-3997', 'tel:+14253673997'),-- ('\uf0e0', '[email protected]', 'mailto:[email protected]'),-- ('\uf0ac', 'alexfrison.net', 'https://alexfrison.net'),-- ('\uf041', 'Seattle / Arlington, WA', None),-- ]-- row_h = (cp_h - 16) / len(items)-- for i, (glyph, text, link) in enumerate(items):-- cy = cp_y + cp_h - 8 - (i + 1) * row_h-- draw_icon_circle(c, cp_x + 10, cy + (row_h - 18) / 2, 18, glyph,-- fg=white, bg=BLUE_SOFT, font_size=10,-- stroke=HexColor('#a8c4ee'))-- c.setFillColor(white)-- c.setFont('Helvetica-Bold', 9.3)-- c.drawString(cp_x + 36, cy + (row_h - 9) / 2 + 1, text)-- if link:-- c.linkURL(link, (cp_x, cy, cp_x + cp_w, cy + row_h),-- relative=0, thickness=0)------def top_feature_card(c, x, y, w, h, glyph, title_html, bullets):-- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)-- draw_icon_circle(c, x + 12, y + h - 38, 24, glyph, fg=BLUE, bg=CARD_BG,-- font_size=12)-- para(c, title_html, ParagraphStyle('ft', parent=CARD_TITLE, fontSize=10.6,-- leading=11.8),-- x + 46, y + h - 11, w - 56, 26)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.7)-- c.line(x + 14, y + h - 46, x + w - 14, y + h - 46)-- html = '<br/>'.join(f'• {b}' for b in bullets)-- para(c, html, ParagraphStyle('fb', parent=CARD_DESC, fontSize=8.0,-- leading=9.2),-- x + 14, y + h - 52, w - 28, 60)------def metric_strip(c, x, y, w, h, metrics):-- round_rect(c, x, y, w, h, 14, CARD_BG, shadow=True)-- n = len(metrics)-- col_w = w / n-- ICON_BG = HexColor('#ddeaf8')-- r_icon = 14 # radius → 28 pt diameter-- for i, (num, lbl, glyph) in enumerate(metrics):-- col_x = x + i * col_w-- if i > 0:-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.7)-- c.line(col_x, y + 8, col_x, y + h - 8)-- # icon circle — left side, vertically centred-- ic_cx = col_x + 8 + r_icon # circle centre x-- ic_cy = y + h / 2 # circle centre y-- c.setFillColor(ICON_BG)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.9)-- c.circle(ic_cx, ic_cy, r_icon, fill=1, stroke=1)-- c.setFont('FA', 14)-- c.setFillColor(BLUE)-- c.drawCentredString(ic_cx, ic_cy - 5, glyph)-- # number — right of icon, upper area, centred in text zone-- tx = col_x + 8 + r_icon * 2 + 6 # left edge of text-- label_w = col_w - (tx - col_x) - 4 # text zone width-- c.setFont('Helvetica-Bold', 18)-- c.setFillColor(BLUE_DARK)-- c.drawCentredString(tx + label_w / 2, y + h - 22, num)-- # label — below number, centred in same text zone-- para(c, lbl, METRIC_LBL, tx, y + h - 24, label_w, 28)------def draw_experience(c, x, y_top, title, date, company, bullets, stack,-- rule_gap=8, width=None):-- if width is None:-- width = PAGE_W - x - 34-- title_w = width - 110-- h = para(c, title, EXP_TITLE, x, y_top, title_w, 22)-- c.setFont('Helvetica-Bold', 10.8)-- c.setFillColor(BLUE)-- c.drawRightString(x + width, y_top - 1, date)-- y = y_top - max(h, 14) - 4-- ch = para(c, company, COMPANY, x, y, width, 14)-- y -= ch + 2-- bullet_html = '<br/>'.join(f'• {b}' for b in bullets)-- bh = para(c, bullet_html, BULLET, x + 8, y, width - 8, 200)-- y -= bh + 3-- sh = para(c, '<font name="FA">\uf013</font> ' + stack, STACK,-- x + 8, y, width - 8, 30)-- bottom = y - sh - rule_gap-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.6)-- c.line(x, bottom, x + width, bottom)-- return bottom - 12------def product_card(c, x, y, w, h, glyph, title, url, desc, link):-- round_rect(c, x, y, w, h, 14, CARD_BG, shadow=True)-- draw_icon_circle(c, x + 10, y + h - 28, 22, glyph, fg=BLUE, bg=CARD_BG,-- font_size=11)-- tx = x + 38-- para(c, title, ParagraphStyle('pt', parent=PRODUCT_TITLE, fontSize=10.0,-- leading=10.8),-- tx, y + h - 8, w - (tx - x) - 10, 14)-- para(c, f'<u>{url}</u>',-- ParagraphStyle('pu', parent=URL_STYLE, fontSize=7.6, leading=8.4),-- tx, y + h - 22, w - (tx - x) - 10, 12)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.5)-- c.line(x + 12, y + h - 34, x + w - 12, y + h - 34)-- para(c, desc,-- ParagraphStyle('pd', parent=CARD_DESC, fontSize=7.7, leading=8.8),-- x + 12, y + h - 38, w - 24, h - 42)-- c.linkURL(link, (x, y, x + w, y + h), relative=0, thickness=0)------def core_card(c, x, y, w, h, glyph, title, desc):-- round_rect(c, x, y, w, h, 12, CARD_BG, shadow=True)-- draw_icon_circle(c, x + 10, y + h - 28, 22, glyph, fg=BLUE, bg=CARD_BG,-- font_size=11)-- para(c, title,-- ParagraphStyle('ct2', parent=CARD_TITLE, fontSize=9.4, leading=10.6),-- x + 40, y + h - 8, w - 50, 18)-- c.setStrokeColor(BLUE_LINE)-- c.setLineWidth(0.5)-- c.line(x + 12, y + h - 32, x + w - 12, y + h - 32)-- para(c, desc,-- ParagraphStyle('cd2', parent=CARD_DESC, fontSize=7.7, leading=9.0),-- x + 12, y + h - 36, w - 24, h - 40)------def links_strip(c, x, y, w, h, links):-- round_rect(c, x, y, w, h, h / 2, CARD_BG, shadow=False)-- cx = x + 14-- for glyph, text, url in links:-- draw_icon_circle(c, cx, y + (h - 18) / 2, 18, glyph, fg=BLUE,-- bg=CARD_BG, font_size=10)-- c.setFont('Helvetica-Bold', 9.2)-- c.setFillColor(BLUE_DARK)-- c.drawString(cx + 24, y + h / 2 - 3, text)-- tw = pdfmetrics.stringWidth(text, 'Helvetica-Bold', 9.2)-- link_w = 24 + tw + 20-- c.linkURL(url, (cx - 4, y, cx + link_w, y + h),-- relative=0, thickness=0)-- cx += link_w + 6------# --- pages -------------------------------------------------------------------def build(path):-- c = canvas.Canvas(path, pagesize=letter)-- c.setTitle('Alex Frison Resume')-- c.setAuthor('Alex Frison')-- c.setSubject('Resume — Data Analyst · Software Engineer · Systems Architect')---- margin = 18---- # ---- page 1 ------ c.setFillColor(BG)-- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)---- header_h = 152-- header_y = PAGE_H - margin - header_h-- draw_header(c, margin, header_y, PAGE_W - 2 * margin, header_h)---- # top feature cards-- gap = 10-- card_w = (PAGE_W - 2 * margin - gap * 3) / 4-- card_h = 92-- top_y = header_y - 8 - card_h-- for i, card in enumerate(TOP_FEATURE_CARDS):-- cx = margin + i * (card_w + gap)-- top_feature_card(c, cx, top_y, card_w, card_h, *card)---- # metrics strip-- ms_h = 52-- ms_y = top_y - 4 - ms_h-- metric_strip(c, margin, ms_y, PAGE_W - 2 * margin, ms_h, METRICS)---- # profile (compact)-- prof_header_y = ms_y - 4 - 24-- section_header(c, margin, prof_header_y, PAGE_W - 2 * margin,-- 'PROFILE', '\uf2c0', h=24)-- # profile box — auto-height based on measured text-- _prof_style = ParagraphStyle('_pp', parent=BODY, fontSize=9.0, leading=10.8)-- _prof_para = Paragraph(PROFILE, _prof_style)-- _, _prof_text_h = _prof_para.wrap(PAGE_W - 2 * margin - 28, 400)-- prof_box_h = int(_prof_text_h) + 18 # 9pt top + 9pt bottom pad-- prof_box_y = prof_header_y - 4 - prof_box_h-- round_rect(c, margin, prof_box_y, PAGE_W - 2 * margin, prof_box_h, 12,-- CARD_BG, shadow=False)-- para(c, PROFILE, _prof_style,-- margin + 14, prof_box_y + prof_box_h - 9,-- PAGE_W - 2 * margin - 28, prof_box_h - 18)---- # experience header-- exp_y = prof_box_y - 8 - 24-- section_header(c, margin, exp_y, PAGE_W - 2 * margin, 'EXPERIENCE',-- '\uf0b1', h=24)-- cur_y = exp_y - 10-- for e in EXPERIENCE[:3]:-- cur_y = draw_experience(c, margin + 12, cur_y, e['title'], e['date'],-- e['company'], e['bullets'], e['stack'],-- width=PAGE_W - 2 * margin - 24)---- # page footer removed to avoid bottom-overlap; pagination is visually obvious.---- c.showPage()---- # ---- page 2 ------ c.setFillColor(BG)-- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)---- y = PAGE_H - margin - 4-- section_header(c, margin, y - 24, PAGE_W - 2 * margin,-- 'EXPERIENCE (CONT.)', '\uf0b1', h=24)-- y -= 36-- for e in EXPERIENCE[3:5]:-- y = draw_experience(c, margin + 12, y, e['title'], e['date'],-- e['company'], e['bullets'], e['stack'],-- width=PAGE_W - 2 * margin - 24)---- # selected products-- section_header(c, margin, y - 10, PAGE_W - 2 * margin,-- 'SELECTED PROJECTS · LIVE EXAMPLES', '\uf0c1', h=24)-- y -= 6-- pw = (PAGE_W - 2 * margin - 10) / 2-- ph = 70-- for idx, prj in enumerate(PROJECTS):-- col = idx % 2-- row = idx // 2-- px = margin + col * (pw + 10)-- py = y - 6 - row * (ph + 6) - ph-- product_card(c, px, py, pw, ph, *prj)-- rows = (len(PROJECTS) + 1) // 2-- y = y - 6 - rows * (ph + 6) - 18---- # core stack-- section_header(c, margin, y - 4, PAGE_W - 2 * margin,-- 'CORE TECHNICAL STACK', '\uf1c0', h=24)-- y -= 6-- cw = (PAGE_W - 2 * margin - 10) / 2-- ch = 70-- for i in range(len(CORE_STACK)):-- col = i % 2-- row = i // 2-- cx = margin + col * (cw + 10)-- cy = y - 6 - row * (ch + 6) - ch-- g, t, d = CORE_STACK[i]-- core_card(c, cx, cy, cw, ch, g, t, d)-- n_rows = (len(CORE_STACK) + 1) // 2-- y = y - 6 - n_rows * (ch + 6) - 18---- # education + links-- section_header(c, margin, y - 4, PAGE_W - 2 * margin,-- 'EDUCATION · LEARNING · ELSEWHERE', '\uf19d', h=24)-- y -= 14-- edu_text = ('<b>Snohomish High School, WA</b> — AP Statistics, '-- 'Calculus, English, Creative Writing, Spanish. Self-taught '-- 'systems builder since age 12 — analytics, '-- 'infrastructure, graphics, photography, hardware, and '-- 'practical problem solving.')-- _, _edu_h = Paragraph(edu_text, LEARN).wrap(PAGE_W - 2 * margin - 28, 60)-- edu_h = int(_edu_h) + 14-- round_rect(c, margin, y - edu_h, PAGE_W - 2 * margin, edu_h, 12, CARD_BG,-- shadow=False)-- para(c, edu_text, LEARN, margin + 14, y - 7, PAGE_W - 2 * margin - 28, _edu_h)-- y -= edu_h + 8---- links_strip(c, margin, y - 34, PAGE_W - 2 * margin, 34, LINKS)---- c.setFont('Helvetica', 7.5)-- c.setFillColor(SUBTLE)-- c.drawCentredString(PAGE_W / 2, 6,-- 'Alex Frison · Resume 2026 · alexfrison.net')---- c.showPage()-- c.save()------if __name__ == '__main__':-- build('Alex_Frison_Resume.pdf')-- print('Wrote Alex_Frison_Resume.pdf')ddeleted file mode 100755BBinary files a/public/Alex_Frison_Resume.pdf and /dev/null differddeleted file mode 100755BBinary files a/public/Alex_Frison_Resume_2026.pdf and /dev/null differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_May2026.pdf and /dev/null differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_May_2026.pdf and /dev/null differddeleted file mode 100755BBinary files a/public/Alex_Frison_Resume_oldddd.dox and /dev/null differddeleted file mode 100644BBinary files a/public/Alex_Frison_Resume_olddddd.pdf and /dev/null differddeleted file mode 100755BBinary files a/public/assets/Alex_Frison_Resume.docx and /dev/null differddeleted file mode 100644BBinary files a/public/assets/Alex_Frison_Resume.pdf and /dev/null differ@@@ -24,7 +24,7 @@</div><div class="contact__social">-- <a class="contact__social-btn contact__social-btn--github" href="https://github.com/HoboStreamer" rel="me noopener" target="_blank">++ <a class="contact__social-btn contact__social-btn--github" href="https://github.com/OpenVibers" rel="me noopener" target="_blank"><i class="fa-brands fa-github"></i><span>GitHub</span></a>@@@ -10,7 +10,7 @@ const year = new Date().getFullYear();<div class="foot__links"><a href="mailto:[email protected]" title="Email"><i class="fa-solid fa-envelope"></i> [email protected]</a><a href="tel:+14253673997" title="Phone"><i class="fa-solid fa-phone"></i> (425) 367-3997</a>-- <a href="https://github.com/HoboStreamer" rel="me noopener" target="_blank" title="GitHub"><i class="fa-brands fa-github"></i> GitHub</a>++ <a href="https://github.com/OpenVibers" rel="me noopener" target="_blank" title="GitHub"><i class="fa-brands fa-github"></i> GitHub</a><a href="https://www.linkedin.com/in/fris/" rel="me noopener" target="_blank" title="LinkedIn"><i class="fa-brands fa-linkedin"></i> LinkedIn</a></div></div>@@@ -25,10 +25,9 @@<span class="hero__rotator" aria-live="polite"><i class="fa-solid fa-chart-line"></i> Data Analyst</span></p><p class="hero__lead">-- Data analyst, systems architect, and sales leader with 10+ years of shipping across industries — from-- SOX-compliance tooling and autonomous AI agents at Amazon ICQA to a-- solo-built 15-microservice open-source creator platform, with roots in-- retail sales management at Verizon Wireless (Sales Rep → Assistant Manager).++ Data analyst and full-stack builder with 10+ years of shipping —++ SOX-compliance tooling and autonomous AI agents at Amazon, a solo-built++ 15-microservice creator platform, and sales leadership at Verizon.Open to new roles.</p><div class="hero__cta">@@@ -39,11 +38,9 @@<div class="hero__chips"><span class="chip"><i class="fa-solid fa-chart-line"></i> Analytics / BI</span><span class="chip"><i class="fa-solid fa-code"></i> Full-Stack Eng</span>-- <span class="chip"><i class="fa-solid fa-robot"></i> Autonomous Agents</span>-- <span class="chip"><i class="fa-solid fa-gear"></i> Robotics / AFM</span>-- <span class="chip"><i class="fa-solid fa-hammer"></i> Product Builder</span>-- <span class="chip"><i class="fa-solid fa-comment-dollar"></i> Sales</span>-- <span class="chip"><i class="fa-solid fa-users"></i> Management / Leadership</span>++ <span class="chip"><i class="fa-solid fa-robot"></i> AI Agents</span>++ <span class="chip"><i class="fa-solid fa-gear"></i> Robotics</span>++ <span class="chip"><i class="fa-solid fa-users"></i> Sales & Leadership</span></div></div>@@@ -113,11 +110,15 @@.hero__portrait { width: min(220px, 55vw); margin-bottom: 8px; }}++ /* Solid color on purpose — background-clip:text + color:transparent breaks++ when Splitting.js wraps chars in inline-block spans (text renders invisible++ on some GPUs/viewports, e.g. Steam Deck at 1280×800). Same bug class as the++ earlier ImpactMetrics "invisible numbers" fix. */.hero__title {-- font-size: clamp(3rem, 9vw, 6.5rem);-- background: linear-gradient(180deg, #fff 30%, #8aa0b6 100%);-- -webkit-background-clip: text; background-clip: text;-- color: transparent;++ font-size: clamp(2.6rem, 7vw, 5.5rem);++ color: var(--fg);++ text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);++ overflow-wrap: break-word;margin-bottom: 0.4em;}.hero__role {@@@ -217,13 +218,10 @@const roles = [`<i class="fa-solid fa-chart-line"></i> Data Analyst`,`<i class="fa-solid fa-code"></i> Software Engineer`,-- `<i class="fa-solid fa-robot"></i> Autonomous Agent Builder`,++ `<i class="fa-solid fa-robot"></i> AI Agent Builder`,`<i class="fa-solid fa-server"></i> Systems Admin`,-- `<i class="fa-solid fa-cube"></i> Product Engineer`,-- `<i class="fa-brands fa-twitch"></i> Streaming Platform Builder`,-- `<i class="fa-solid fa-tree"></i> Pacific Northwest Native`,-- `<i class="fa-solid fa-comment-dollar"></i> Sales`,-- `<i class="fa-solid fa-users"></i> Management / Leadership`++ `<i class="fa-solid fa-users"></i> Sales Leader`,++ `<i class="fa-solid fa-tree"></i> Pacific Northwest Native`];const rot = document.querySelector(".hero__rotator");if (rot) {@@@ -1,10 +1,10 @@---const metrics = [-- { num: "1,000+", label: "SOX-compliance IRDRs hand-researched at Amazon ICQA", icon: "fa-solid fa-magnifying-glass-chart" },-- { num: "15", label: "OpenVibe microservices designed, built, and shipped", icon: "fa-solid fa-cubes-stacked" },-- { num: "200K+", label: "Live game servers tracked across GameServerStats", icon: "fa-solid fa-tower-broadcast" },-- { num: "20+", label: "Amazon TamperMonkey/Python automation scripts", icon: "fa-solid fa-code" },-- { num: "10+", label: "Years running my own LLC end-to-end", icon: "fa-solid fa-building-flag" },++ { num: "1,000+", label: "SOX-compliance IRDRs researched at Amazon", icon: "fa-solid fa-magnifying-glass-chart" },++ { num: "15", label: "OpenVibe microservices built solo", icon: "fa-solid fa-cubes-stacked" },++ { num: "200K+", label: "Live game servers tracked", icon: "fa-solid fa-tower-broadcast" },++ { num: "20+", label: "Automation scripts for Amazon workflows", icon: "fa-solid fa-code" },++ { num: "10+", label: "Years running my own LLC", icon: "fa-solid fa-building-flag" },];---<section class="impact" aria-labelledby="impact-title" data-reveal>@@@ -5,10 +5,10 @@"end": "2025","location": "PAE2 · Arlington, WA","bullets": [-- "Own SOX-compliance IRDR (Inventory Discrepancy Research Report) workflows — hand-researched 1,000+ inventory mismatches and built the data systems that support Sarbanes-Oxley audit requirements.",-- "Built ICQA SQL and Excel reporting for inventory accuracy, discrepancy review, and defect visibility; surfaced findings in dashboards and recurring leadership reports.",-- "Authored an Anthropic-powered SQL copilot that contextually references every available data table for analysts.",-- "Built and shipped an autonomous root-cause-analysis bot that performs full end-to-end inventory research."++ "Owned SOX-compliance IRDR workflows — researched 1,000+ inventory discrepancies and built the data systems behind Sarbanes-Oxley audit requirements.",++ "Built SQL and Excel reporting for inventory accuracy and defect visibility; delivered dashboards and recurring leadership reports.",++ "Created an Anthropic-powered SQL copilot that references every available data table for analysts.",++ "Shipped an autonomous root-cause bot that performs end-to-end inventory research."],"stack": ["SQL", "Excel", "Python", "Anthropic API", "TamperMonkey", "Power Query", "SOX Compliance"],"featured": true,@@@ -5,9 +5,9 @@"end": "2024","location": "Amazon Robotics launch site","bullets": [-- "Monitored Amazon Robotics (ARS) automation floor during launch-stage operations — enforcing amnesty zones, clearing obstructions, and maintaining safe pod/drive flow.",-- "Investigated pod, drive unit, and workstation issues on the floor; escalated recurring defects and documented patterns to support process improvement.",-- "Coordinated amnesty events, ensured safe robot re-entry after floor interventions, and communicated status to ops leadership in real time."++ "Monitored the Amazon Robotics floor during launch operations — enforced amnesty zones, cleared obstructions, and kept pod/drive flow safe.",++ "Investigated pod, drive unit, and workstation issues; documented recurring defects to drive process improvement.",++ "Coordinated amnesty events and communicated floor status to ops leadership in real time."],"stack": ["Amazon Robotics", "AFM protocols", "Floor safety", "Amnesty ops"],"featured": true,@@@ -4,10 +4,10 @@"start": "2020","end": "2023","bullets": [-- "Consistently ranked among top performers in the district — hit and exceeded monthly quotas across smartphones, plans, accessories, device protection, financing, and Verizon Visa card offers.",-- "Promoted to Assistant Manager; mentored and coached a team of 4–8 reps through weekly 1-on-1s, floor coaching, and monthly skill workshops covering objection handling, upselling, and customer retention.",-- "Led store opens/closes, managed inventory, resolved escalated customer issues, and ran daily huddles to align the team on promos and targets.",-- "Built genuine rapport with customers — turned walk-ins into multi-line accounts and walk-outs into long-term brand advocates through consultative, needs-first selling."++ "Ranked among the district's top performers — consistently exceeded monthly quotas across phones, plans, accessories, protection, and financing.",++ "Promoted to Assistant Manager; coached a team of 4–8 reps through 1-on-1s, floor coaching, and skill workshops.",++ "Ran store opens/closes, inventory, escalations, and daily huddles.",++ "Turned walk-ins into multi-line accounts through consultative, needs-first selling."],"stack": ["Consultative Sales", "Team Leadership", "Coaching", "Retail Ops", "Quota Attainment", "Customer Retention"],"order": 3@@@ -4,12 +4,12 @@"start": "2022","end": "2025","bullets": [-- "Sole architect and engineer on OpenVibe — a 15-microservice open-source platform kernel: distributed event bus, OIDC/OAuth2 identity provider, capability contract registry, media object store, live-streaming ingest and restream, real-time chat, billing ledger, AI/search backbone, and realtime infrastructure. Self-hosted alternative to Twitch + YouTube + Patreon + Discord. 16 phases documented; 11+ fully implemented with cutover runbook and migration tooling complete.",-- "Built and operated HoboStreamer from scratch — a full live-streaming platform with WebRTC SFU, HLS, multi-destination RTMP restream (OBS → Twitch/YouTube/Kick simultaneously), auto-recorded VODs, clip detection, real-time chat with custom emotes, voice channels, stream analytics, comprehensive moderation tools, and an integrated browser game for viewers. Fully open source.",-- "Built HoboQuest — a browser MMORPG with a custom isomorphic game engine: wrote all game logic once in shared.js, compiled to both server and client via a custom MD5-diffing compiler. Source Engine-style lag compensation (client-side prediction + server reconciliation) drives the real-time multiplayer. Server-master.js orchestrates 4 independent game instances with distinct rulesets (PvP world, social hub, farm island, ship minigame). Custom Canvas 2D rendering engine with SAT collision detection. Open-world survival: building, combat, NPC AI, 7+ skills, inventory/crafting/bank, bus fast travel. In-game scratch card and slot casino; mobile touch and gamepad input. Integrated live into HoboStreamer — viewers watch and play in the same session.",-- "Maintained hobo.tools — central SSO and OAuth2 identity network across 14 interconnected services, including shared auth, service registry, image processing, maps, audio, and cross-domain session management."++ "Sole architect of OpenVibe — a 15-microservice open-source platform kernel: event bus, OIDC/OAuth2 identity, media storage, live-streaming ingest/restream, real-time chat, billing ledger, and AI/search backbone. A self-hosted alternative to Twitch + YouTube + Patreon + Discord.",++ "Built and operated HoboStreamer — a full live-streaming platform with WebRTC, HLS, multi-destination RTMP restream, auto-recorded VODs, clips, real-time chat, voice channels, and moderation tools. Fully open source.",++ "Built HoboQuest — a browser MMORPG on a custom isomorphic engine: shared game logic compiled to both server and client, Source Engine-style lag compensation, and four live game worlds. Playable in a tab, streamed live inside HoboStreamer.",++ "Ran hobo.tools — shared SSO/OAuth2 identity and service registry connecting 14 services."],-- "stack": ["TypeScript", "Node.js", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "Lua", "C#", "React", "Vue"],++ "stack": ["TypeScript", "Node.js", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "React", "Vue"],"featured": true,"order": 4}@@@ -1,7 +1,7 @@---title: "Amazon SQL Copilot"category: "amazon"--summary: "Anthropic-powered chatbot that generates SQL queries, intelligently referencing context from every available database table for ICQA data analysts."++summary: "Anthropic-powered copilot that writes runnable, schema-aware SQL for ICQA analysts — no memorizing 200+ table names."tech: ["Python", "Anthropic API", "SQL", "Schema Indexing", "Streamlit"]role: "Sole builder"year: "2025"@@@ -1,7 +1,7 @@---title: "Amazon Userscript Suite"category: "amazon"--summary: "Family of TamperMonkey userscripts that supercharge the daily ICQA + AFM workflow: IRDRTools, FCQuality Apex Ultra, FC/Prosearch, GCAHistory, FCLM Indirect, Oculus."++summary: "Six TamperMonkey userscripts that remove daily friction from internal ICQA and AFM tooling — deep links, table enrichment, bulk actions."tech: ["JavaScript", "TamperMonkey", "DOM APIs", "Internal Web Tools"]role: "Sole builder"year: "2024–2026"@@@ -1,7 +1,7 @@---title: "HoboQuest"category: "hobo"--summary: "Browser MMORPG with a custom isomorphic game engine, Source Engine-style lag compensation, and 4-instance server architecture — no downloads, open a tab and play."++summary: "Browser MMORPG on a custom-built engine with Source-style lag compensation — no downloads, open a tab and play."tech: ["JavaScript", "Node.js", "WebSockets", "Canvas API", "Express", "isomorphic compiler", "lag compensation", "Canvas 2D physics"]role: "Founder / Engine & Gameplay Engineer"year: "2023–Present"@@@ -11,30 +11,16 @@ featured: trueorder: 12-----A persistent browser MMORPG built on a custom-engineered isomorphic game engine — no Unity, no Godot, no game framework. Everything from the physics and collision system to the multiplayer protocol was written from scratch.++A persistent browser MMORPG built on a custom engine — no Unity, no Godot. Physics, collision, and the multiplayer protocol were all written from scratch.--**Custom isomorphic compile pipeline.** All game logic lives in a single `shared.js` file containing conditional compilation blocks (`/*IF_SERVER*/.../*IF_END*/`). A custom `compiler.js` transforms it into `server.js` and `public/client.js` — the same physics, entity, and world state logic runs on both sides, preventing desyncs by design. MD5 change detection skips unnecessary recompiles.++**Isomorphic engine.** All game logic lives in one shared file; a custom compiler emits both the server and client builds, so the exact same physics and world-state code runs on both sides — desyncs are prevented by design.--**Source Engine-style lag compensation.** Real-time multiplayer uses client-side prediction with server reconciliation — the same methodology used in Counter-Strike and Valve's Source Engine. Movement and combat feel instant for the player while the server remains authoritative over all state.++**Source Engine-style netcode.** Client-side prediction with server reconciliation (the Counter-Strike approach): movement and combat feel instant while the server stays authoritative.--**Multi-instance server architecture.** A `server-master.js` orchestrator spawns four independent game-server processes (ports 9000–9003), each running a different map with its own ruleset:--- Default world — PvP enabled, player building enabled--- Central city — social hub with vendors; PvP disabled--- Farm island — resource farming; PvP enabled, building disabled--- Ship minigame — dedicated activity instance++**Four live worlds.** An orchestrator runs four game instances with distinct rulesets — a PvP building world, a safe social hub, a farming island, and a ship minigame.--**Canvas 2D rendering engine.** Built a custom Canvas 2D rendering layer (`lib/c2d.js`) with a full 2D separating-axis theorem (SAT) collision detection system, polygon/circle collision, voronoi region edge detection, and frame-accurate hit resolution — no third-party physics library.++**Custom rendering & physics.** Canvas 2D renderer with full SAT collision detection (polygon/circle, frame-accurate hits) — no third-party physics library.--**Open-world survival systems.** Tile-based 512×512 open world with:--- **Building**: place and upgrade walls, doors, beds, tool cupboards, campfires, barbed wire--- **Combat**: melee knockback, ranged pistol, arrows with ballistic gravity arc, NPC aggro with patrol/attack telegraph/hit flash--- **Skills**: Woodcutting, Mining, Herbology, Attack, and more — persistent per player--- **Economy**: inventory, crafting recipes, equipment slots, bank system, bus system for zone travel++**Full survival loop.** Building, combat (melee, ranged, NPC AI), persistent skills, crafting, banking, fast travel — plus an in-game scratch-card and slots casino with server-validated state.--**In-game casino.** Scratch card and slot machine minigames with a credit balance system, SoundManager 2 audio integration, and server-validated game state.----**Cross-platform input.** Mobile touch controls overlay (tap/joystick buttons) with runtime touch detection, plus gamepad detection with a controller configuration UI. Desktop WASD + mouse.----**Integrated with HoboStreamer.** The game is embedded live in the streaming platform — viewers watch and play simultaneously, with chat bridged between the game and the stream.----The collaborative pixel canvas is a separate 512×512 real-time art board for territory battles and group art, also accessible from [hobo.quest](https://hobo.quest).++**Play anywhere.** Desktop WASD + mouse, mobile touch overlay, and gamepad support. Embedded live in HoboStreamer, so viewers watch and play in the same session.@@@ -1,7 +1,7 @@---title: "Autonomous IRDR Root-Cause Bot"category: "amazon"--summary: "Fully autonomous inventory root-cause-analysis agent that plans, gathers evidence, and writes a complete IRDR research report end-to-end — supporting SOX-compliant inventory accuracy."++summary: "Autonomous agent that plans, gathers evidence, and writes complete SOX-compliant inventory research reports — hours of analysis down to seconds."tech: ["Python", "Anthropic API", "Tool-use Agents", "TamperMonkey", "SQL", "SOX Compliance"]role: "Sole builder"year: "2025"@@@ -1,7 +1,7 @@---title: "OpenVibe"category: "hobo"--summary: "Open-source creator platform kernel: 15+ microservices, distributed event bus, OIDC/OAuth2 identity, live streaming, media storage, billing ledger, AI backbone, and real-time infrastructure — sole architect and engineer across 16 documented phases."++summary: "Open-source creator platform kernel — 15+ microservices spanning identity, live streaming, media, billing, and AI, designed and built by one person."tech: ["Node.js", "TypeScript", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "REST APIs", "Event-Driven Architecture", "Microservices"]role: "Sole Architect & Principal Engineer"year: "2024–2026"@@@ -10,24 +10,16 @@ featured: trueorder: 5-----A fully open-source, self-hosted platform kernel designed to replace the Hobo Network and serve as a production-grade alternative to the entire creator economy stack — Twitch, YouTube, Patreon, Reddit, and Discord in a single deployable monorepo. Built by one person across 16 documented phases, with cutover runbook and migration tooling complete.++A fully open-source, self-hosted platform kernel — Twitch, YouTube, Patreon, and Discord in one deployable monorepo. Designed and built by one person across 16 documented phases.--**Services shipped (13 microservices):**++**What's in it:**--- **openvibe-network** — Control plane: OIDC/OAuth2 identity provider, service registry, capability contract registry, user-module namespace API, policy engine, and host-aware routing across `auth.openvibe.network`, `api.openvibe.network`, `admin.openvibe.network`--- **openre-stream** — RTMP ingest and multi-destination restream (OBS → Twitch / YouTube / Kick / any RTMP endpoint simultaneously)--- **openvibe-live** — Live streaming frontend: SSR viewer surface, DVR, viewer analytics, stream presence--- **openvibe-media** — Media object store: VODs, clips, thumbnails, stream snapshots, paste/file storage — with namespace registry, derivative pipeline, quota enforcement, and lifecycle audit log--- **openvibe-chat** — Real-time chat: moderation, purges, bans, custom emotes, channel threading--- **openvibe-community** — Forums, reactions, community posts, Discord-style channels, paste versioning--- **openvibe-billing** — Credits ledger, tips, VIP status tiers, payout engine, SOX-style audit trail--- **openvibe-ai** — AI/search backbone: autonomous content tagging, SEO generation, analyst copilot, embedding-based search--- **openvibe-games** — Embedded browser game surface (HoboQuest integration, live viewer participation)--- **openvibe-events** — Distributed event bus: full audit envelope (`event_id`, `trace_id`, `version`, `actor`), topic + subscription filtering, ack/retry work queue, dead-letter tables--- **openvibe-realtime** — WebSocket hub, pub/sub presence layer, fan-out infrastructure--- **openvibe-workers** — Task queue, scheduled jobs, background processors, queue-native pipeline--- **openvibe-content** — Content workflows: publishing pipelines, moderation queue, product workflow seams++- **Identity & control plane** — OIDC/OAuth2 provider, service registry, capability contracts, policy engine++- **Live streaming** — RTMP ingest, multi-destination restream (OBS → Twitch / YouTube / Kick simultaneously), DVR, viewer analytics++- **Media** — object store for VODs, clips, and thumbnails with derivative pipeline, quotas, and audit log++- **Community** — real-time chat with moderation and custom emotes, forums, Discord-style channels++- **Billing** — credits ledger, tips, VIP tiers, payout engine with SOX-style audit trail++- **AI & search** — autonomous content tagging, SEO generation, embedding-based search++- **Infrastructure** — distributed event bus with full audit envelope, WebSocket/pub-sub realtime layer, task queues, and shared typed-contract packages (SQLite + PostgreSQL dual-mode persistence)--**Shared packages:** `openvibe-contracts` (typed event schemas), `openvibe-sdk` (cross-service clients), `openvibe-persistence` (SQLite + PostgreSQL dual-mode with schema parity enforcement), `openvibe-queue`, `openvibe-runtime`, `openvibe-storage`, `openvibe-redis`, `openvibe-observability`----**Phase completion:** 11+ of 16 phases fully implemented. Remaining phases paused at production-readiness stage — cutover runbook written, migration tooling from HoboStreamer/hobo.tools complete, schema drift checks green. Servers brought offline in May 2026 due to funding constraints during job search. The architecture is sound and the code is all there.++**Status:** 11+ of 16 phases fully implemented; cutover runbook and migration tooling complete. Servers offline since May 2026 during job search — the architecture is sound and the code is all there.@@@ -64,7 +64,7 @@ const pageUrl = canonical ?? new URL(Astro.url.pathname, Astro.site).toString();"addressCountry": "US"},"sameAs": [-- "https://github.com/HoboStreamer",++ "https://github.com/OpenVibers","https://www.linkedin.com/in/fris/"]}@@@ -21,30 +21,24 @@ import ContactBlock from "../components/ContactBlock.astro";<h2 class="section-title">A versatile builder</h2><p class="profile-para"><i class="fa-solid fa-microchip profile-para__icon"></i>-- <span>Data analyst and systems architect with 10+ years of shipping — from-- <strong>SOX-compliance tooling at Amazon ICQA</strong> to autonomous-- AI research agents to a <strong>15-microservice open-source platform-- kernel</strong>, all built and operated solo. Strong at translating-- operational context into production-grade tooling, stakeholder-ready-- analysis, and systems that actually scale.</span>++ <span>Data analyst and systems architect with 10+ years of shipping —++ from <strong>SOX-compliance tooling at Amazon ICQA</strong> to++ autonomous AI agents to a <strong>15-microservice open-source++ platform</strong>, built and operated solo. I turn operational++ context into tools and analysis stakeholders can act on.</span></p><p class="profile-para"><i class="fa-solid fa-store profile-para__icon"></i>-- <span>Before the tech career, I cut my teeth in retail sales at-- <strong>Verizon Wireless</strong> — first as a Sales Representative,-- then promoted to <strong>Assistant Manager</strong>. Quota-driven-- environment, high customer volume, team coaching, and the kind of-- communication pressure that either breaks you or makes you sharp.-- It made me sharp. That foundation — reading people, closing clearly,-- handling objections — still shows up every time I present data or-- walk a stakeholder through a decision.</span>++ <span>Before tech, I sold at <strong>Verizon Wireless</strong> —++ promoted from Sales Rep to <strong>Assistant Manager</strong> in a++ quota-driven store. Reading people, handling objections, and closing++ clearly still shape how I present data and decisions.</span></p><p class="profile-para"><i class="fa-solid fa-screwdriver-wrench profile-para__icon"></i>-- <span>Tech-geek since 12, building computers and chasing bleeding-edge-- hardware. A decade running my own LLC, ten years tracking 200,000+-- live game servers, and 1,000+ IRDRs personally researched at Amazon.-- I ship fast and keep what works.</span>++ <span>Building computers since age 12. A decade running my own LLC,++ ten years tracking 200,000+ live game servers, 1,000+ IRDRs++ researched at Amazon. I ship fast and keep what works.</span></p></div><aside class="card profile__facts" data-reveal>@@@ -54,7 +48,7 @@ import ContactBlock from "../components/ContactBlock.astro";<li><strong><i class="fa-solid fa-briefcase"></i> Open to</strong>Hybrid · Remote · On-site PNW</li><li><strong><i class="fa-solid fa-building-columns"></i> Founder</strong>GameServerStats, LLC</li><li><strong><i class="fa-solid fa-laptop-code"></i> Last role</strong>Amazon · Data Analyst (ICQA)</li>-- <li class="profile__facts-seeking"><strong><i class="fa-solid fa-magnifying-glass"></i> Seeking</strong><span class="seek-groups"><span class="seek-group"><i class="fa-solid fa-chart-line"></i> Analyst · Data Engineer · BI</span><span class="seek-group"><i class="fa-solid fa-server"></i> IT · Systems · Robotics · Technician</span><span class="seek-group"><i class="fa-solid fa-people-group"></i> Sales · Leadership · Management</span><span class="seek-group"><i class="fa-solid fa-hammer"></i> Builder · Architect · Maker</span></span></li>++ <li class="profile__facts-seeking"><strong><i class="fa-solid fa-magnifying-glass"></i> Seeking</strong><span class="seek-groups"><span class="seek-group"><i class="fa-solid fa-chart-line"></i> Analyst · Data Engineer · BI</span><span class="seek-group"><i class="fa-solid fa-server"></i> IT · Systems · Robotics</span><span class="seek-group"><i class="fa-solid fa-people-group"></i> Sales · Leadership · Management</span></span></li></ul></aside></div>
deleted file mode 100755
deleted file mode 100755
@@ -1,299 +0,0 @@
@@ -1,299 +0,0 @@
-from reportlab.pdfbase import pdfmetrics
-from reportlab.pdfbase.ttfonts import TTFont
-from reportlab.lib.pagesizes import letter
-from reportlab.lib.colors import HexColor, white
-from reportlab.pdfgen import canvas
-from reportlab.platypus import Paragraph
-from reportlab.lib.styles import ParagraphStyle
-
-pdfmetrics.registerFont(TTFont('FA', 'fontawesome-webfont.ttf'))
-
-PAGE_W, PAGE_H = letter
-
-# Palette
-BG = HexColor('#eef2f7')
-BLUE = HexColor('#4f84d6')
-BLUE_DARK = HexColor('#2a4f90')
-BLUE_LINE = HexColor('#7ea8ee')
-BLUE_SOFT = HexColor('#5f92de')
-TEXT = HexColor('#34548f')
-SHADOW = HexColor('#d9dee8')
-CARD_BG = HexColor('#f8fbff')
-
-# Typography
-BODY = ParagraphStyle('Body', fontName='Helvetica', fontSize=10.4, leading=12.1, textColor=TEXT, spaceAfter=0)
-COMPANY = ParagraphStyle('Company', fontName='Helvetica-Bold', fontSize=9.8, leading=11, textColor=BLUE)
-BULLET = ParagraphStyle('Bullet', fontName='Helvetica', fontSize=9.15, leading=10.25, textColor=TEXT)
-CARD_TITLE = ParagraphStyle('CardTitle', fontName='Helvetica-Bold', fontSize=10.9, leading=12.2, textColor=BLUE_DARK)
-CARD_DESC = ParagraphStyle('CardDesc', fontName='Helvetica', fontSize=8.35, leading=9.15, textColor=TEXT)
-PRODUCT_TITLE = ParagraphStyle('ProductTitle', fontName='Helvetica-Bold', fontSize=10.2, leading=11.4, textColor=BLUE_DARK)
-URL_STYLE = ParagraphStyle('URL', fontName='Helvetica', fontSize=8.7, leading=9.4, textColor=BLUE)
-LEARN = ParagraphStyle('Learn', fontName='Helvetica', fontSize=8.7, leading=9.8, textColor=TEXT)
-EXP_TITLE = ParagraphStyle('ExpTitle', fontName='Helvetica-Bold', fontSize=14.6, leading=16.0, textColor=BLUE_DARK)
-
-products = [
- ('\uf0ac','HoboTools','hobo.tools','Shared-account ecosystem and network hub','https://hobo.tools'),
- ('\uf03d','HoboStreamer','hobostreamer.com','Streaming, clips, VODs, chat, creator tools','https://hobostreamer.com'),
- ('\uf041','HoboMaps','maps.hobo.tools','Map overlays, research, and survival data','https://maps.hobo.tools'),
- ('\uf11b','HoboQuest','hobo.quest','Browser MMORPG, multiplayer games, live canvas','https://hobo.quest'),
-]
-
-core_cards = [
- ('\uf201','Analytics / Data','SQL, Excel, reporting, dashboards, defect research, integrity, and root-cause analysis'),
- ('\uf121','Engineering / Web','JavaScript, TypeScript, Node.js, Python, PHP, React, Vue, Electron, HTML5, CSS/SASS, REST APIs'),
- ('\uf1c0','Systems / Infrastructure','Linux, Windows Server, NGINX, Apache, MongoDB, Redis, Cloudflare, deployment, scaling, and DDoS resilience'),
- ('\uf11b','Game Dev / Product','Lua, C#, C++, gameplay systems, creator tooling, UX, graphics, and product thinking'),
-]
-
-
-def round_rect(c, x, y, w, h, r, fill, stroke=BLUE_LINE, stroke_width=1, shadow=True, shadow_dx=4, shadow_dy=-4):
- if shadow:
- c.setFillColor(SHADOW)
- c.setStrokeColor(SHADOW)
- c.roundRect(x+shadow_dx, y+shadow_dy, w, h, r, fill=1, stroke=0)
- c.setFillColor(fill)
- c.setStrokeColor(stroke)
- c.setLineWidth(stroke_width)
- c.roundRect(x, y, w, h, r, fill=1, stroke=1)
-
-
-def para(c, html, style, x, top, width, height=200):
- p = Paragraph(html, style)
- _, h = p.wrap(width, height)
- p.drawOn(c, x, top-h)
- return h
-
-
-def draw_icon_circle(c, x, y, size, glyph, fg=BLUE, bg=CARD_BG, font_size=15):
- c.setFillColor(bg)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(1.1)
- c.circle(x + size/2, y + size/2, size/2, fill=1, stroke=1)
- c.setFont('FA', font_size)
- c.setFillColor(fg)
- c.drawCentredString(x + size/2, y + size/2 - font_size*0.34, glyph)
-
-
-def section_header(c, x, y, w, text, glyph):
- round_rect(c, x, y, w, 30, 15, BLUE, stroke=BLUE, stroke_width=0.8, shadow=True)
- draw_icon_circle(c, x+10, y+4, 22, glyph, fg=white, bg=BLUE_SOFT, font_size=12)
- c.setFont('Helvetica-Bold', 15)
- c.setFillColor(white)
- c.drawString(x+38, y+9, text)
-
-
-def chip_width(text):
- return pdfmetrics.stringWidth(text, 'Helvetica-Bold', 8.9) + 28
-
-
-def draw_chip(c, x, y, text, glyph, h=22):
- w = chip_width(text)
- c.setFillColor(BLUE_SOFT)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.8)
- c.roundRect(x, y, w, h, h/2, fill=1, stroke=1)
- c.setFont('FA', 10)
- c.setFillColor(white)
- c.drawString(x+9, y+6, glyph)
- c.setFont('Helvetica-Bold', 8.9)
- c.drawString(x+22, y+7, text)
- return w
-
-
-def draw_header(c, x, y, w, h):
- round_rect(c, x, y, w, h, 28, BLUE, stroke=BLUE, stroke_width=0.8, shadow=True)
- c.setFillColor(white)
- c.setFont('Helvetica-Bold', 31)
- c.drawString(x+20, y+h-46, 'Alex Frison')
-
- subtitle_style = ParagraphStyle('subtitle', fontName='Helvetica-Bold', fontSize=10.3, leading=11.0, textColor=white)
- subtitle = ('Data analyst and systems-minded builder across analytics, robotics support, retail sales leadership, and full-stack platforms.')
- para(c, subtitle, subtitle_style, x+22, y+h-74, 330, 40)
-
- # Chips - always inside the header; fixed 2-row layout
- row1 = [('\uf201','Analytics / BI'),('\uf121','Full Stack Eng'),('\uf0ad','Robotics / RME')]
- row2 = [('\uf07a','Sales Leadership'),('\uf0b1','Product Builder')]
- cx = x + 22
- for glyph, text in row1:
- cx += draw_chip(c, cx, y+42, text, glyph) + 10
- cx = x + 22
- for glyph, text in row2:
- cx += draw_chip(c, cx, y+14, text, glyph) + 10
-
- # contact panel
- cp_w, cp_h = 206, 84
- cp_x, cp_y = x + w - cp_w - 18, y + h - cp_h - 18
- round_rect(c, cp_x, cp_y, cp_w, cp_h, 18, BLUE_SOFT, stroke=BLUE_LINE, stroke_width=0.9, shadow=False)
- top_icon_y = cp_y + cp_h - 36
- bottom_icon_y = cp_y + 12
- draw_icon_circle(c, cp_x+12, top_icon_y, 24, '\uf095', fg=white, bg=BLUE_SOFT, font_size=12)
- draw_icon_circle(c, cp_x+12, bottom_icon_y, 24, '\uf0e0', fg=white, bg=BLUE_SOFT, font_size=11)
- c.setStrokeColor(HexColor('#83a8e6'))
- c.setLineWidth(0.8)
- c.line(cp_x+18, cp_y + cp_h/2, cp_x+cp_w-18, cp_y + cp_h/2)
- c.setFillColor(white)
- c.setFont('Helvetica-Bold', 10.5)
- c.drawString(cp_x+42, cp_y + cp_h - 20, '+1 (425) 367-3997')
- c.drawString(cp_x+42, cp_y + 28, '[email protected]')
-
-
-def top_feature_card(c, x, y, w, h, glyph, title_html, bullets, desc_size=8.45):
- round_rect(c, x, y, w, h, 18, CARD_BG, shadow=True)
- draw_icon_circle(c, x+14, y+h-46, 28, glyph, fg=BLUE, bg=CARD_BG, font_size=14)
- para(c, title_html, ParagraphStyle('ft', parent=CARD_TITLE, fontSize=11.2, leading=12.5), x+52, y+h-14, w-66, 26)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.8)
- c.line(x+18, y+h-52, x+w-18, y+h-52)
- html = '<br/>'.join(f'• {b}' for b in bullets)
- para(c, html, ParagraphStyle('fb', parent=CARD_DESC, fontSize=desc_size, leading=9.1), x+18, y+h-60, w-36, 60)
-
-
-def product_card(c, x, y, w, h, glyph, title, url, desc, link):
- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)
- draw_icon_circle(c, x+12, y+h-16-22, 22, glyph, fg=BLUE, bg=CARD_BG, font_size=11)
- tx = x + 42
- para(c, title, ParagraphStyle('pt', parent=PRODUCT_TITLE, fontSize=10.1, leading=10.9), tx, y+h-10, w-(tx-x)-12, 18)
- para(c, f'<u>{url}</u>', ParagraphStyle('pu', parent=URL_STYLE, fontSize=8.6, leading=8.9), tx, y+h-30, w-(tx-x)-12, 14)
- para(c, desc, ParagraphStyle('pd', parent=CARD_DESC, fontSize=8.4, leading=8.8), tx, y+18, w-(tx-x)-12, 24)
- c.linkURL(link, (x, y, x+w, y+h), relative=0, thickness=0)
-
-
-def core_card(c, x, y, w, h, glyph, title, desc):
- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)
- draw_icon_circle(c, x+12, y+h-14-22, 22, glyph, fg=BLUE, bg=CARD_BG, font_size=12)
- para(c, title, ParagraphStyle('ct2', parent=CARD_TITLE, fontSize=10.8, leading=11.4), x+42, y+h-10, w-54, 18)
- # More left margin and slightly lower top to separate from title.
- para(c, desc, ParagraphStyle('cd2', parent=CARD_DESC, fontSize=8.45, leading=9.0), x+58, y+30, w-70, 24)
-
-
-def draw_experience(c, x, y_top, title, date, company, bullets, rule_gap=10):
- title_style = ParagraphStyle('et', parent=EXP_TITLE, fontSize=14.6, leading=16.0)
- title_w = PAGE_W - x - 34 - 132
- h = para(c, title, title_style, x, y_top, title_w, 24)
- c.setFont('Helvetica-Bold', 11.8)
- c.setFillColor(BLUE)
- c.drawRightString(PAGE_W-34, y_top-1, date)
- y = y_top - max(h, 14) - 7
- ch = para(c, company, COMPANY, x, y, PAGE_W-x-34, 14)
- y -= ch + 3
- bullet_html = '<br/>'.join(f'• {b}' for b in bullets)
- bh = para(c, bullet_html, BULLET, x+10, y, PAGE_W-x-44, 60)
- bottom = y - bh - rule_gap
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.8)
- c.line(x, bottom, PAGE_W-34, bottom)
- return bottom - 14
-
-
-def build(path):
- c = canvas.Canvas(path, pagesize=letter)
- c.setTitle('Alex Frison Resume')
-
- for page in [1, 2]:
- c.setFillColor(BG)
- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
- x = 18
- if page == 1:
- header_h = 170
- header_y = PAGE_H - 18 - header_h
- draw_header(c, x, header_y, PAGE_W-36, header_h)
-
- # top feature cards, tighter to header to reduce blank band
- top_y = header_y - 8 - 126
- gap = 14
- card_w = (PAGE_W - 36 - gap*3) / 4
- card_h = 126
- xs = [x, x+card_w+gap, x+(card_w+gap)*2, x+(card_w+gap)*3]
- top_feature_card(c, xs[0], top_y, card_w, card_h, '\uf201', 'Analytics /<br/>Data', [
- 'SQL, Excel, Inventory Control / Quality Assurance reporting',
- 'Defect review and discrepancy analysis',
- 'Live dashboards and analytics'
- ])
- top_feature_card(c, xs[1], top_y, card_w, card_h, '\uf0ad', 'RME /<br/>Robotics', [
- 'Amazon Robotics FC launch and automation',
- 'Robotics support and troubleshooting',
- 'Electro-mech hardware exposure'
- ])
- top_feature_card(c, xs[2], top_y, card_w, card_h, '\uf07a', 'Sales /<br/>Leadership', [
- 'Plans, devices, financing',
- 'Coaching, meetings, escalations',
- 'Retention and quota execution'
- ])
- top_feature_card(c, xs[3], top_y, card_w, card_h, '\uf03d', 'Product /<br/>Streaming', [
- 'HoboTools, HoboStreamer, HoboQuest',
- 'Clips, VODs, creator tools',
- 'Cross-service UX and operations'
- ], desc_size=8.0)
-
- profile_header_y = top_y - 22 - 16
- section_header(c, x, profile_header_y, PAGE_W-36, 'PROFILE', '\uf2c0')
- profile_box_y = profile_header_y - 50
- round_rect(c, x, profile_box_y-4, PAGE_W-36, 46, 18, CARD_BG, shadow=False)
- profile = ('Versatile data analyst and systems-minded builder with experience across Amazon ICQA analytics, Amazon RME support, retail sales leadership, and full-stack product development. Strong at turning operational context into clear reporting, stakeholder-ready analysis, practical tools, and process improvements.')
- para(c, profile, BODY, x+18, profile_box_y+38, PAGE_W-72, 30)
-
- exp_header_y = profile_box_y - 38
- section_header(c, x, exp_header_y, PAGE_W-36, 'EXPERIENCE', '\uf0b1')
- cur_y = exp_header_y - 16
- cur_y = draw_experience(c, x+18, cur_y, 'Data Analyst', '2024 - Present', 'Amazon', [
- 'Built ICQA SQL and Excel reporting for inventory accuracy, discrepancy review, and defect visibility.',
- 'Turned mismatch findings into dashboards, recurring reports, and stakeholder-ready analysis for operational decisions.'
- ])
- cur_y = draw_experience(c, x+18, cur_y, 'Reliability, Maintenance, and Engineering', '2023 - 2024', 'Amazon', [
- 'Supported launch-stage ARS robotics and conveyance operations while monitoring automation flow, floor conditions, and equipment behavior.',
- 'Assisted troubleshooting across robotics, uptime, safety, and electro-mechanical issues in a high-change launch environment.'
- ])
- draw_experience(c, x+18, cur_y, 'Assistant Manager / Sales Representative', '2020 - 2023', 'Verizon Wireless', [
- 'Sold smartphones, wireless plans, accessories, protection, financing, and credit-card offers in a quota-driven retail environment; earned district-level recognition.',
- 'Coached associates, ran monthly meetings and workshops, and improved customer service, retention, merchandising, and inventory ownership.'
- ], rule_gap=8)
- else:
- y = PAGE_H - 26
- y = draw_experience(c, x, y, 'Founder / Product Engineer / Streaming & Game Builder', 'Present', 'Hobo Tools / HoboStreamer / HoboQuest / HoboMaps', [
- 'Built shared-account web products across Hobo Tools, HoboStreamer, HoboQuest, and HoboMaps with unified UX, connected identity, and cross-service navigation.',
- 'Developed creator-facing streaming systems including clips, VODs, chat, moderation, analytics, restream support, and multi-path delivery; also built browser multiplayer game experiences and creator tools using Lua, C#, C++, gameplay systems, and product-minded UX.'
- ])
- y = draw_experience(c, x, y, 'Founder / Developer / Systems Admin', '2015 - Present', 'GameServerStats, LLC', [
- 'Built analytics-focused web platforms that track 200,000+ live game servers using JavaScript, Node.js, PHP, Python, SQL, MongoDB, Redis, HTML, and CSS.',
- 'Designed ingestion, search, dashboards, Linux infrastructure, scaling, DDoS resilience, support workflows, and business reporting.'
- ])
- y = draw_experience(c, x, y, 'Earlier Technical and Business Experience', '2012 - Present', 'Devolved, GModStore.com, eBay', [
- 'Built multiplayer game systems, digital products, graphics, support docs, e-commerce listings, moderation workflows, and monetized online communities.'
- ], rule_gap=6)
-
- prod_header_y = y - 16
- section_header(c, x, prod_header_y, PAGE_W-36, 'SELECTED PRODUCTS / LIVE EXAMPLES', '\uf0c1')
- pw = (PAGE_W - 36 - 18) / 2
- ph = 66
- py1 = prod_header_y - 72
- xs = [x, x+pw+18]
- product_card(c, xs[0], py1, pw, ph, *products[0])
- product_card(c, xs[1], py1, pw, ph, *products[1])
- py2 = py1 - 76
- product_card(c, xs[0], py2, pw, ph, *products[2])
- product_card(c, xs[1], py2, pw, ph, *products[3])
-
- core_header_y = py2 - 28
- section_header(c, x, core_header_y, PAGE_W-36, 'CORE TECHNICAL STACK', '\uf1c0')
- cw = (PAGE_W - 36 - 18) / 2
- ch = 58
- cy1 = core_header_y - 64
- core_card(c, x, cy1, cw, ch, core_cards[0][0], core_cards[0][1], core_cards[0][2])
- core_card(c, x+cw+18, cy1, cw, ch, core_cards[1][0], core_cards[1][1], core_cards[1][2])
- cy2 = cy1 - 66
- core_card(c, x, cy2, cw, ch, core_cards[2][0], core_cards[2][1], core_cards[2][2])
- core_card(c, x+cw+18, cy2, cw, ch, core_cards[3][0], core_cards[3][1], core_cards[3][2])
-
- learn_header_y = cy2 - 28
- section_header(c, x, learn_header_y, PAGE_W-36, 'EDUCATION / LEARNING', '\uf19d')
- learn_box_y = learn_header_y - 48
- round_rect(c, x, learn_box_y, PAGE_W-36, 40, 16, CARD_BG, shadow=False)
- learn_text = ('Snohomish High School, WA - AP Statistics, Calculus, English, Creative Writing, Spanish - Self-taught systems builder with a strong self-starter mindset, focused on analytics, infrastructure, graphics, photography, and practical problem solving.')
- para(c, learn_text, LEARN, x+16, learn_box_y+27, PAGE_W-68, 20)
-
- c.showPage()
- c.save()
-
-
-if __name__ == '__main__':
- build('Alex_Frison_Resume.pdf')
deleted file mode 100644
deleted file mode 100644
@@ -1,593 +0,0 @@
@@ -1,593 +0,0 @@
-"""
-build_resume_v13.py — Alex Frison resume (2026 edition).
-
-Improvements over v12:
-- Pulls the rich detail that lives on alexfrison.net (SOX/IRDR work,
- Anthropic SQL copilot, autonomous root-cause bot, OpenVibe 15-microservice
- kernel, HoboQuest custom engine, GameServerStats LLC scale, Verizon
- district-level recognition).
-- New "Impact at a glance" metrics strip on page 1 (recruiter-skim friendly).
-- Tighter typography, denser content, fewer wasted bands of blank space.
-- Page 1 = pitch + first three roles (recent + revenue-relevant).
-- Page 2 = remaining roles, six featured projects with live URLs, full
- technical stack, education + links footer.
-- Same palette so it stays visually consistent with the site.
-
-Run:
- cd /opt/repos/alexfrison.net && python build_resume_v13.py
-Outputs:
- Alex_Frison_Resume.pdf (next to the script)
-"""
-
-from reportlab.pdfbase import pdfmetrics
-from reportlab.pdfbase.ttfonts import TTFont
-from reportlab.lib.pagesizes import letter
-from reportlab.lib.colors import HexColor, white
-from reportlab.pdfgen import canvas
-from reportlab.platypus import Paragraph
-from reportlab.lib.styles import ParagraphStyle
-
-pdfmetrics.registerFont(TTFont('FA', 'fontawesome-webfont.ttf'))
-
-PAGE_W, PAGE_H = letter
-
-# --- palette ----------------------------------------------------------------
-BG = HexColor('#eef2f7')
-BLUE = HexColor('#4f84d6')
-BLUE_DARK = HexColor('#2a4f90')
-BLUE_LINE = HexColor('#7ea8ee')
-BLUE_SOFT = HexColor('#5f92de')
-TEXT = HexColor('#34548f')
-SUBTLE = HexColor('#5a73a8')
-SHADOW = HexColor('#d9dee8')
-CARD_BG = HexColor('#f8fbff')
-ACCENT_GOLD = HexColor('#c98b1f') # used only for metric numerals
-
-# --- styles -----------------------------------------------------------------
-BODY = ParagraphStyle('Body', fontName='Helvetica', fontSize=9.6, leading=11.4,
- textColor=TEXT, spaceAfter=0)
-COMPANY = ParagraphStyle('Company', fontName='Helvetica-Bold', fontSize=9.6,
- leading=11, textColor=BLUE)
-LOCATION = ParagraphStyle('Location', fontName='Helvetica-Oblique', fontSize=8.6,
- leading=10, textColor=SUBTLE)
-BULLET = ParagraphStyle('Bullet', fontName='Helvetica', fontSize=8.6,
- leading=10.0, textColor=TEXT)
-STACK = ParagraphStyle('Stack', fontName='Helvetica-Oblique', fontSize=8.2,
- leading=10, textColor=SUBTLE)
-CARD_TITLE = ParagraphStyle('CardTitle', fontName='Helvetica-Bold', fontSize=10.7,
- leading=12, textColor=BLUE_DARK)
-CARD_DESC = ParagraphStyle('CardDesc', fontName='Helvetica', fontSize=8.35,
- leading=9.6, textColor=TEXT)
-PRODUCT_TITLE = ParagraphStyle('ProductTitle', fontName='Helvetica-Bold',
- fontSize=10.0, leading=11.2, textColor=BLUE_DARK)
-URL_STYLE = ParagraphStyle('URL', fontName='Helvetica', fontSize=8.4,
- leading=9.2, textColor=BLUE)
-LEARN = ParagraphStyle('Learn', fontName='Helvetica', fontSize=8.7,
- leading=10, textColor=TEXT)
-EXP_TITLE = ParagraphStyle('ExpTitle', fontName='Helvetica-Bold', fontSize=13.4,
- leading=15.4, textColor=BLUE_DARK)
-METRIC_NUM = ParagraphStyle('MetricNum', fontName='Helvetica-Bold', fontSize=22,
- leading=24, textColor=BLUE_DARK, alignment=1)
-METRIC_LBL = ParagraphStyle('MetricLbl', fontName='Helvetica', fontSize=7.8,
- leading=9.2, textColor=TEXT, alignment=1)
-
-# --- content ---------------------------------------------------------------
-PROFILE = (
- "Data analyst and systems architect with 10+ years building and shipping "
- "production systems. <b>1,000+ SOX-compliance IRDRs personally researched "
- "at Amazon ICQA</b>; built an Anthropic SQL copilot and autonomous "
- "root-cause-analysis agent in daily use. Solo architect of a "
- "<b>15-microservice open-source platform kernel</b> (OpenVibe), a "
- "live-streaming stack (HoboStreamer), and a 14-service identity network. "
- "Previously <b>district top performer → Assistant Manager at Verizon "
- "Wireless</b>, coaching 4–8 reps."
-)
-
-METRICS = [
- ('1,000+', 'SOX IRDRs hand-researched', '\uf002'),
- ('15', 'OpenVibe microservices shipped', '\uf233'),
- ('200K+', 'Live game servers tracked', '\uf11b'),
- ('20+', 'Amazon TamperMonkey/<br/>Python scripts','\uf085'),
- ('10+', 'Years running my own LLC', '\uf0b1'),
-]
-
-TOP_FEATURE_CARDS = [
- ('\uf201', 'Analytics /<br/>Data', [
- 'SQL, Power Query, Excel, dashboards',
- 'SOX-compliant IRDR research + LLM agents',
- ]),
- ('\uf085', 'Robotics /<br/>RME & AFM', [
- 'Amazon Robotics (ARS) floor monitor',
- 'Pod / drive / workstation troubleshooting',
- ]),
- ('\uf07a', 'Sales /<br/>Leadership', [
- 'Top performer; district-level recognition',
- 'Assistant Manager; coached 4–8 reps',
- ]),
- ('\uf03d', 'Product /<br/>Engineering', [
- 'OpenVibe: 15-service platform kernel',
- 'HoboStreamer, HoboQuest, Hobo Tools',
- ]),
-]
-
-EXPERIENCE = [
- {
- 'title': 'Data Analyst — ICQA',
- 'date': '2024 - 2025',
- 'company': 'Amazon · PAE2, Arlington WA',
- 'bullets': [
- 'Own SOX-compliance IRDR workflow — <b>hand-researched 1,000+ inventory mismatches</b>, authored reporting satisfying Sarbanes-Oxley audit trails.',
- 'Authored an Anthropic-powered <b>SQL copilot</b> + an <b>autonomous root-cause-analysis agent</b> that plans, gathers evidence, and writes complete IRDRs end-to-end — collapsing hours of manual research per defect into seconds.',
- ],
- 'stack': 'SQL · Excel / Power Query · Python · Anthropic API · TamperMonkey · SOX Compliance',
- },
- {
- 'title': 'Amnesty Floor Monitor (AFM) — Amazon Robotics',
- 'date': '2023 - 2024',
- 'company': 'Amazon · Robotics launch site',
- 'bullets': [
- 'Monitored Amazon Robotics (ARS) automation during launch-stage operations — enforced amnesty zones, cleared obstructions, kept pod and drive-unit flow safe.',
- 'Investigated recurring pod, drive, and workstation defects on the floor; documented patterns and escalated to RME and ops leadership to drive process improvements.',
- ],
- 'stack': 'Amazon Robotics · AFM protocols · Floor safety · Defect documentation',
- },
- {
- 'title': 'Assistant Manager / Sales Representative',
- 'date': '2020 - 2023',
- 'company': 'Verizon Wireless',
- 'bullets': [
- 'Consistently ranked among <b>top performers in the district</b> — hit and exceeded monthly quotas across smartphones, plans, accessories, device protection, financing, and Verizon Visa offers.',
- 'Promoted to Assistant Manager; mentored and coached a team of 4–8 reps through weekly 1-on-1s, floor coaching, and monthly workshops on objection handling, upselling, and customer retention. Led store opens / closes, inventory, escalations, and daily huddles.',
- ],
- 'stack': 'Consultative Sales · Team Leadership · Coaching · Retail Ops · Quota Attainment · Customer Retention',
- },
- {
- 'title': 'Founder · Systems Architect · Principal Engineer',
- 'date': '2022 - Present',
- 'company': 'Hobo Network / OpenVibe',
- 'bullets': [
- 'Sole architect and engineer on <b>OpenVibe</b> — a 15-microservice open-source platform kernel: distributed event bus, OIDC/OAuth2 identity, media object store, live ingest + restream, real-time chat, billing ledger, AI/search backbone. Self-hosted alternative to Twitch + YouTube + Patreon + Discord. 16 phases documented, 11+ shipped.',
- '<b>HoboStreamer</b>: live-streaming platform from scratch — WebRTC SFU, HLS, multi-destination RTMP restream (OBS → Twitch / YouTube / Kick), auto-recorded VODs, clip detection, chat, voice channels, analytics, moderation, and an embedded in-stream browser game. Fully open source.',
- '<b>HoboQuest</b> (browser MMORPG) + <b>hobo.tools</b> (SSO + OAuth2 across 14 services): custom isomorphic engine with Source-Engine-style client prediction, 4 live game instances; shared themes, notifications, cross-domain sessions.',
- ],
- 'stack': 'TypeScript · Node.js · Express · PostgreSQL / SQLite · Redis · WebRTC · WebSockets · OAuth2 / OIDC · FFmpeg · Lua · C#',
- },
- {
- 'title': 'Founder · Developer · Systems Admin',
- 'date': '2015 - Present',
- 'company': 'GameServerStats, LLC',
- 'bullets': [
- 'Built analytics platforms tracking <b>200,000+ live game servers</b>; #1 Google result for several major titles, licensed to third-party sites under written agreement.',
- 'Self-built and collocated 1U / 2U servers in datacenters; designed ingestion, dashboards, DDoS resilience, and ran the entire LLC — licenses, taxes, partner relations — for 10+ years.',
- ],
- 'stack': 'Node.js · PHP · Python · MongoDB · MySQL · Redis · NGINX · Linux',
- },
- {
- 'title': 'Earlier Technical & Business Experience',
- 'date': '2012 - 2015',
- 'company': 'Devolved · GModStore · eBay',
- 'bullets': [
- 'Built multiplayer game systems, in-game economies, and digital products sold through ScriptFodder / GModStore.',
- 'Created graphics, support docs, e-commerce listings, moderation workflows; ran online communities at scale.',
- ],
- 'stack': 'Lua · PHP · MySQL · Photoshop · Source Engine',
- },
-]
-
-PROJECTS = [
- ('\uf135', 'OpenVibe',
- 'github.com/HoboStreamer/OpenVibe-Temporary-MonoRepo',
- '15-microservice open-source platform kernel — identity, event bus, media store, live ingest, billing, AI/search. Sole architect across 16 phases.',
- 'https://github.com/HoboStreamer/OpenVibe-Temporary-MonoRepo'),
- ('\uf0eb', 'Autonomous IRDR Root-Cause Bot',
- 'Internal Amazon tool',
- 'Autonomous agent that plans, gathers evidence, and files SOX-compliant IRDRs end-to-end — built on 1,000+ hand-researched reports.',
- 'https://alexfrison.net/portfolio'),
- ('\uf1c0', 'Amazon SQL Copilot',
- 'Internal Amazon tool',
- 'Anthropic-powered SQL assistant that auto-discovers tables / FKs / semantics and streams runnable, table-aware SQL for ICQA analysts.',
- 'https://alexfrison.net/portfolio'),
- ('\uf03d', 'HoboStreamer',
- 'hobostreamer.com',
- 'Live streaming platform from scratch — WebRTC + HLS, multi-RTMP restream, VODs, clips, chat, voice, analytics. 100% open source.',
- 'https://hobostreamer.com'),
-]
-
-CORE_STACK = [
- ('\uf201', 'Analytics / Data',
- 'SQL · Excel + Power Query + VBA · Python (pandas) · Dashboards · ICQA reporting · Root-cause analysis · Anthropic / LLM tool-use agents'),
- ('\uf121', 'Engineering / Web',
- 'JavaScript / TypeScript · Node.js · Express · Python · PHP · React · Vue · REST APIs · HTML5 / CSS · WebSockets / WebRTC · OAuth2 / OIDC'),
- ('\uf233', 'Systems / Infrastructure',
- 'Linux · NGINX · PostgreSQL / MySQL / MongoDB / Redis · Cloudflare · DDoS resilience · Self-built 1U / 2U servers'),
- ('\uf085', 'Robotics · Game Dev · Leadership',
- 'ARS robotics support · Electro-mechanical troubleshooting · Lua / C# / C++ · Source Engine · Team coaching'),
-]
-
-LINKS = [
- ('\uf0ac', 'alexfrison.net', 'https://alexfrison.net'),
- ('\uf0e0', '[email protected]', 'mailto:[email protected]'),
- ('\uf095', '(425) 367-3997', 'tel:+14253673997'),
- ('\uf08c', 'linkedin.com/in/fris', 'https://www.linkedin.com/in/fris/'),
-]
-
-
-# --- primitives ------------------------------------------------------------
-def round_rect(c, x, y, w, h, r, fill, stroke=BLUE_LINE, stroke_width=1,
- shadow=True, shadow_dx=4, shadow_dy=-4):
- if shadow:
- c.setFillColor(SHADOW)
- c.setStrokeColor(SHADOW)
- c.roundRect(x + shadow_dx, y + shadow_dy, w, h, r, fill=1, stroke=0)
- c.setFillColor(fill)
- c.setStrokeColor(stroke)
- c.setLineWidth(stroke_width)
- c.roundRect(x, y, w, h, r, fill=1, stroke=1)
-
-
-def para(c, html, style, x, top, width, height=400):
- p = Paragraph(html, style)
- _, h = p.wrap(width, height)
- p.drawOn(c, x, top - h)
- return h
-
-
-def draw_icon_circle(c, x, y, size, glyph, fg=BLUE, bg=CARD_BG,
- font_size=15, stroke=BLUE_LINE):
- c.setFillColor(bg)
- c.setStrokeColor(stroke)
- c.setLineWidth(1.0)
- c.circle(x + size / 2, y + size / 2, size / 2, fill=1, stroke=1)
- c.setFont('FA', font_size)
- c.setFillColor(fg)
- c.drawCentredString(x + size / 2, y + size / 2 - font_size * 0.34, glyph)
-
-
-def section_header(c, x, y, w, text, glyph, h=26):
- round_rect(c, x, y, w, h, h / 2, BLUE, stroke=BLUE, stroke_width=0.8,
- shadow=True)
- draw_icon_circle(c, x + 8, y + (h - 20) / 2, 20, glyph, fg=white,
- bg=BLUE_SOFT, font_size=11, stroke=BLUE_SOFT)
- c.setFont('Helvetica-Bold', 13)
- c.setFillColor(white)
- c.drawString(x + 36, y + (h - 13) / 2 + 1, text)
-
-
-def chip_width(text):
- return pdfmetrics.stringWidth(text, 'Helvetica-Bold', 8.9) + 28
-
-
-def draw_chip(c, x, y, text, glyph, h=20):
- w = chip_width(text)
- c.setFillColor(BLUE_SOFT)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.8)
- c.roundRect(x, y, w, h, h / 2, fill=1, stroke=1)
- c.setFont('FA', 10)
- c.setFillColor(white)
- c.drawString(x + 9, y + 5, glyph)
- c.setFont('Helvetica-Bold', 8.9)
- c.drawString(x + 22, y + 6, text)
- return w
-
-
-def draw_header(c, x, y, w, h):
- round_rect(c, x, y, w, h, 26, BLUE, stroke=BLUE, stroke_width=0.8,
- shadow=True)
- c.setFillColor(white)
- c.setFont('Helvetica-Bold', 30)
- c.drawString(x + 22, y + h - 42, 'Alex Frison')
-
- role_style = ParagraphStyle('role', fontName='Helvetica-Bold',
- fontSize=10.4, leading=12.2, textColor=white)
- role = ('Data Analyst · Software Engineer · '
- 'Systems Architect')
- para(c, role, role_style, x + 24, y + h - 50, 340, 14)
-
- subtitle_style = ParagraphStyle('subtitle', fontName='Helvetica',
- fontSize=9.0, leading=11.0,
- textColor=white)
- subtitle = ('Versatile builder across analytics, robotics support, '
- 'retail sales leadership, and full-stack platforms. '
- 'Pacific Northwest based — open to hybrid, remote, on-site.')
- para(c, subtitle, subtitle_style, x + 24, y + h - 64, 340, 30)
-
- # role chips, two rows
- row1 = [('\uf201', 'Analytics / BI'),
- ('\uf121', 'Full Stack Eng'),
- ('\uf085', 'Robotics / RME')]
- row2 = [('\uf07a', 'Sales Leadership'),
- ('\uf0b1', 'Product Builder'),
- ('\uf135', 'Systems Architect')]
- cx = x + 24
- for glyph, text in row1:
- cx += draw_chip(c, cx, y + 36, text, glyph) + 8
- cx = x + 24
- for glyph, text in row2:
- cx += draw_chip(c, cx, y + 12, text, glyph) + 8
-
- # contact panel
- cp_w, cp_h = 212, 92
- cp_x, cp_y = x + w - cp_w - 18, y + h - cp_h - 16
- round_rect(c, cp_x, cp_y, cp_w, cp_h, 16, BLUE_SOFT,
- stroke=BLUE_LINE, stroke_width=0.9, shadow=False)
-
- # three rows: phone / email / web
- items = [
- ('\uf095', '+1 (425) 367-3997', 'tel:+14253673997'),
- ('\uf0e0', '[email protected]', 'mailto:[email protected]'),
- ('\uf0ac', 'alexfrison.net', 'https://alexfrison.net'),
- ('\uf041', 'Seattle / Arlington, WA', None),
- ]
- row_h = (cp_h - 16) / len(items)
- for i, (glyph, text, link) in enumerate(items):
- cy = cp_y + cp_h - 8 - (i + 1) * row_h
- draw_icon_circle(c, cp_x + 10, cy + (row_h - 18) / 2, 18, glyph,
- fg=white, bg=BLUE_SOFT, font_size=10,
- stroke=HexColor('#a8c4ee'))
- c.setFillColor(white)
- c.setFont('Helvetica-Bold', 9.3)
- c.drawString(cp_x + 36, cy + (row_h - 9) / 2 + 1, text)
- if link:
- c.linkURL(link, (cp_x, cy, cp_x + cp_w, cy + row_h),
- relative=0, thickness=0)
-
-
-def top_feature_card(c, x, y, w, h, glyph, title_html, bullets):
- round_rect(c, x, y, w, h, 16, CARD_BG, shadow=True)
- draw_icon_circle(c, x + 12, y + h - 38, 24, glyph, fg=BLUE, bg=CARD_BG,
- font_size=12)
- para(c, title_html, ParagraphStyle('ft', parent=CARD_TITLE, fontSize=10.6,
- leading=11.8),
- x + 46, y + h - 11, w - 56, 26)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.7)
- c.line(x + 14, y + h - 46, x + w - 14, y + h - 46)
- html = '<br/>'.join(f'• {b}' for b in bullets)
- para(c, html, ParagraphStyle('fb', parent=CARD_DESC, fontSize=8.0,
- leading=9.2),
- x + 14, y + h - 52, w - 28, 60)
-
-
-def metric_strip(c, x, y, w, h, metrics):
- round_rect(c, x, y, w, h, 14, CARD_BG, shadow=True)
- n = len(metrics)
- col_w = w / n
- ICON_BG = HexColor('#ddeaf8')
- r_icon = 14 # radius → 28 pt diameter
- for i, (num, lbl, glyph) in enumerate(metrics):
- col_x = x + i * col_w
- if i > 0:
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.7)
- c.line(col_x, y + 8, col_x, y + h - 8)
- # icon circle — left side, vertically centred
- ic_cx = col_x + 8 + r_icon # circle centre x
- ic_cy = y + h / 2 # circle centre y
- c.setFillColor(ICON_BG)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.9)
- c.circle(ic_cx, ic_cy, r_icon, fill=1, stroke=1)
- c.setFont('FA', 14)
- c.setFillColor(BLUE)
- c.drawCentredString(ic_cx, ic_cy - 5, glyph)
- # number — right of icon, upper area, centred in text zone
- tx = col_x + 8 + r_icon * 2 + 6 # left edge of text
- label_w = col_w - (tx - col_x) - 4 # text zone width
- c.setFont('Helvetica-Bold', 18)
- c.setFillColor(BLUE_DARK)
- c.drawCentredString(tx + label_w / 2, y + h - 22, num)
- # label — below number, centred in same text zone
- para(c, lbl, METRIC_LBL, tx, y + h - 24, label_w, 28)
-
-
-def draw_experience(c, x, y_top, title, date, company, bullets, stack,
- rule_gap=8, width=None):
- if width is None:
- width = PAGE_W - x - 34
- title_w = width - 110
- h = para(c, title, EXP_TITLE, x, y_top, title_w, 22)
- c.setFont('Helvetica-Bold', 10.8)
- c.setFillColor(BLUE)
- c.drawRightString(x + width, y_top - 1, date)
- y = y_top - max(h, 14) - 4
- ch = para(c, company, COMPANY, x, y, width, 14)
- y -= ch + 2
- bullet_html = '<br/>'.join(f'• {b}' for b in bullets)
- bh = para(c, bullet_html, BULLET, x + 8, y, width - 8, 200)
- y -= bh + 3
- sh = para(c, '<font name="FA">\uf013</font> ' + stack, STACK,
- x + 8, y, width - 8, 30)
- bottom = y - sh - rule_gap
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.6)
- c.line(x, bottom, x + width, bottom)
- return bottom - 12
-
-
-def product_card(c, x, y, w, h, glyph, title, url, desc, link):
- round_rect(c, x, y, w, h, 14, CARD_BG, shadow=True)
- draw_icon_circle(c, x + 10, y + h - 28, 22, glyph, fg=BLUE, bg=CARD_BG,
- font_size=11)
- tx = x + 38
- para(c, title, ParagraphStyle('pt', parent=PRODUCT_TITLE, fontSize=10.0,
- leading=10.8),
- tx, y + h - 8, w - (tx - x) - 10, 14)
- para(c, f'<u>{url}</u>',
- ParagraphStyle('pu', parent=URL_STYLE, fontSize=7.6, leading=8.4),
- tx, y + h - 22, w - (tx - x) - 10, 12)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.5)
- c.line(x + 12, y + h - 34, x + w - 12, y + h - 34)
- para(c, desc,
- ParagraphStyle('pd', parent=CARD_DESC, fontSize=7.7, leading=8.8),
- x + 12, y + h - 38, w - 24, h - 42)
- c.linkURL(link, (x, y, x + w, y + h), relative=0, thickness=0)
-
-
-def core_card(c, x, y, w, h, glyph, title, desc):
- round_rect(c, x, y, w, h, 12, CARD_BG, shadow=True)
- draw_icon_circle(c, x + 10, y + h - 28, 22, glyph, fg=BLUE, bg=CARD_BG,
- font_size=11)
- para(c, title,
- ParagraphStyle('ct2', parent=CARD_TITLE, fontSize=9.4, leading=10.6),
- x + 40, y + h - 8, w - 50, 18)
- c.setStrokeColor(BLUE_LINE)
- c.setLineWidth(0.5)
- c.line(x + 12, y + h - 32, x + w - 12, y + h - 32)
- para(c, desc,
- ParagraphStyle('cd2', parent=CARD_DESC, fontSize=7.7, leading=9.0),
- x + 12, y + h - 36, w - 24, h - 40)
-
-
-def links_strip(c, x, y, w, h, links):
- round_rect(c, x, y, w, h, h / 2, CARD_BG, shadow=False)
- cx = x + 14
- for glyph, text, url in links:
- draw_icon_circle(c, cx, y + (h - 18) / 2, 18, glyph, fg=BLUE,
- bg=CARD_BG, font_size=10)
- c.setFont('Helvetica-Bold', 9.2)
- c.setFillColor(BLUE_DARK)
- c.drawString(cx + 24, y + h / 2 - 3, text)
- tw = pdfmetrics.stringWidth(text, 'Helvetica-Bold', 9.2)
- link_w = 24 + tw + 20
- c.linkURL(url, (cx - 4, y, cx + link_w, y + h),
- relative=0, thickness=0)
- cx += link_w + 6
-
-
-# --- pages -----------------------------------------------------------------
-def build(path):
- c = canvas.Canvas(path, pagesize=letter)
- c.setTitle('Alex Frison Resume')
- c.setAuthor('Alex Frison')
- c.setSubject('Resume — Data Analyst · Software Engineer · Systems Architect')
-
- margin = 18
-
- # ---- page 1 ----
- c.setFillColor(BG)
- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
-
- header_h = 152
- header_y = PAGE_H - margin - header_h
- draw_header(c, margin, header_y, PAGE_W - 2 * margin, header_h)
-
- # top feature cards
- gap = 10
- card_w = (PAGE_W - 2 * margin - gap * 3) / 4
- card_h = 92
- top_y = header_y - 8 - card_h
- for i, card in enumerate(TOP_FEATURE_CARDS):
- cx = margin + i * (card_w + gap)
- top_feature_card(c, cx, top_y, card_w, card_h, *card)
-
- # metrics strip
- ms_h = 52
- ms_y = top_y - 4 - ms_h
- metric_strip(c, margin, ms_y, PAGE_W - 2 * margin, ms_h, METRICS)
-
- # profile (compact)
- prof_header_y = ms_y - 4 - 24
- section_header(c, margin, prof_header_y, PAGE_W - 2 * margin,
- 'PROFILE', '\uf2c0', h=24)
- # profile box — auto-height based on measured text
- _prof_style = ParagraphStyle('_pp', parent=BODY, fontSize=9.0, leading=10.8)
- _prof_para = Paragraph(PROFILE, _prof_style)
- _, _prof_text_h = _prof_para.wrap(PAGE_W - 2 * margin - 28, 400)
- prof_box_h = int(_prof_text_h) + 18 # 9pt top + 9pt bottom pad
- prof_box_y = prof_header_y - 4 - prof_box_h
- round_rect(c, margin, prof_box_y, PAGE_W - 2 * margin, prof_box_h, 12,
- CARD_BG, shadow=False)
- para(c, PROFILE, _prof_style,
- margin + 14, prof_box_y + prof_box_h - 9,
- PAGE_W - 2 * margin - 28, prof_box_h - 18)
-
- # experience header
- exp_y = prof_box_y - 8 - 24
- section_header(c, margin, exp_y, PAGE_W - 2 * margin, 'EXPERIENCE',
- '\uf0b1', h=24)
- cur_y = exp_y - 10
- for e in EXPERIENCE[:3]:
- cur_y = draw_experience(c, margin + 12, cur_y, e['title'], e['date'],
- e['company'], e['bullets'], e['stack'],
- width=PAGE_W - 2 * margin - 24)
-
- # page footer removed to avoid bottom-overlap; pagination is visually obvious.
-
- c.showPage()
-
- # ---- page 2 ----
- c.setFillColor(BG)
- c.rect(0, 0, PAGE_W, PAGE_H, fill=1, stroke=0)
-
- y = PAGE_H - margin - 4
- section_header(c, margin, y - 24, PAGE_W - 2 * margin,
- 'EXPERIENCE (CONT.)', '\uf0b1', h=24)
- y -= 36
- for e in EXPERIENCE[3:5]:
- y = draw_experience(c, margin + 12, y, e['title'], e['date'],
- e['company'], e['bullets'], e['stack'],
- width=PAGE_W - 2 * margin - 24)
-
- # selected products
- section_header(c, margin, y - 10, PAGE_W - 2 * margin,
- 'SELECTED PROJECTS · LIVE EXAMPLES', '\uf0c1', h=24)
- y -= 6
- pw = (PAGE_W - 2 * margin - 10) / 2
- ph = 70
- for idx, prj in enumerate(PROJECTS):
- col = idx % 2
- row = idx // 2
- px = margin + col * (pw + 10)
- py = y - 6 - row * (ph + 6) - ph
- product_card(c, px, py, pw, ph, *prj)
- rows = (len(PROJECTS) + 1) // 2
- y = y - 6 - rows * (ph + 6) - 18
-
- # core stack
- section_header(c, margin, y - 4, PAGE_W - 2 * margin,
- 'CORE TECHNICAL STACK', '\uf1c0', h=24)
- y -= 6
- cw = (PAGE_W - 2 * margin - 10) / 2
- ch = 70
- for i in range(len(CORE_STACK)):
- col = i % 2
- row = i // 2
- cx = margin + col * (cw + 10)
- cy = y - 6 - row * (ch + 6) - ch
- g, t, d = CORE_STACK[i]
- core_card(c, cx, cy, cw, ch, g, t, d)
- n_rows = (len(CORE_STACK) + 1) // 2
- y = y - 6 - n_rows * (ch + 6) - 18
-
- # education + links
- section_header(c, margin, y - 4, PAGE_W - 2 * margin,
- 'EDUCATION · LEARNING · ELSEWHERE', '\uf19d', h=24)
- y -= 14
- edu_text = ('<b>Snohomish High School, WA</b> — AP Statistics, '
- 'Calculus, English, Creative Writing, Spanish. Self-taught '
- 'systems builder since age 12 — analytics, '
- 'infrastructure, graphics, photography, hardware, and '
- 'practical problem solving.')
- _, _edu_h = Paragraph(edu_text, LEARN).wrap(PAGE_W - 2 * margin - 28, 60)
- edu_h = int(_edu_h) + 14
- round_rect(c, margin, y - edu_h, PAGE_W - 2 * margin, edu_h, 12, CARD_BG,
- shadow=False)
- para(c, edu_text, LEARN, margin + 14, y - 7, PAGE_W - 2 * margin - 28, _edu_h)
- y -= edu_h + 8
-
- links_strip(c, margin, y - 34, PAGE_W - 2 * margin, 34, LINKS)
-
- c.setFont('Helvetica', 7.5)
- c.setFillColor(SUBTLE)
- c.drawCentredString(PAGE_W / 2, 6,
- 'Alex Frison · Resume 2026 · alexfrison.net')
-
- c.showPage()
- c.save()
-
-
-if __name__ == '__main__':
- build('Alex_Frison_Resume.pdf')
- print('Wrote Alex_Frison_Resume.pdf')
deleted file mode 100755
deleted file mode 100755
Binary files a/public/Alex_Frison_Resume.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume.pdf and /dev/null differ
deleted file mode 100755
deleted file mode 100755
Binary files a/public/Alex_Frison_Resume_2026.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_2026.pdf and /dev/null differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_May2026.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_May2026.pdf and /dev/null differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_May_2026.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_May_2026.pdf and /dev/null differ
deleted file mode 100755
deleted file mode 100755
Binary files a/public/Alex_Frison_Resume_oldddd.dox and /dev/null differ
Binary files a/public/Alex_Frison_Resume_oldddd.dox and /dev/null differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/Alex_Frison_Resume_olddddd.pdf and /dev/null differ
Binary files a/public/Alex_Frison_Resume_olddddd.pdf and /dev/null differ
deleted file mode 100755
deleted file mode 100755
Binary files a/public/assets/Alex_Frison_Resume.docx and /dev/null differ
Binary files a/public/assets/Alex_Frison_Resume.docx and /dev/null differ
deleted file mode 100644
deleted file mode 100644
Binary files a/public/assets/Alex_Frison_Resume.pdf and /dev/null differ
Binary files a/public/assets/Alex_Frison_Resume.pdf and /dev/null differ
@@ -24,7 +24,7 @@
@@ -24,7 +24,7 @@
</div>
</div>
<div class="contact__social">
<div class="contact__social">
- <a class="contact__social-btn contact__social-btn--github" href="https://github.com/HoboStreamer" rel="me noopener" target="_blank">
+ <a class="contact__social-btn contact__social-btn--github" href="https://github.com/OpenVibers" rel="me noopener" target="_blank">
<i class="fa-brands fa-github"></i>
<i class="fa-brands fa-github"></i>
<span>GitHub</span>
<span>GitHub</span>
</a>
</a>
@@ -10,7 +10,7 @@ const year = new Date().getFullYear();
@@ -10,7 +10,7 @@ const year = new Date().getFullYear();
<div class="foot__links">
<div class="foot__links">
<a href="mailto:[email protected]" title="Email"><i class="fa-solid fa-envelope"></i> [email protected]</a>
<a href="mailto:[email protected]" title="Email"><i class="fa-solid fa-envelope"></i> [email protected]</a>
<a href="tel:+14253673997" title="Phone"><i class="fa-solid fa-phone"></i> (425) 367-3997</a>
<a href="tel:+14253673997" title="Phone"><i class="fa-solid fa-phone"></i> (425) 367-3997</a>
- <a href="https://github.com/HoboStreamer" rel="me noopener" target="_blank" title="GitHub"><i class="fa-brands fa-github"></i> GitHub</a>
+ <a href="https://github.com/OpenVibers" rel="me noopener" target="_blank" title="GitHub"><i class="fa-brands fa-github"></i> GitHub</a>
<a href="https://www.linkedin.com/in/fris/" rel="me noopener" target="_blank" title="LinkedIn"><i class="fa-brands fa-linkedin"></i> LinkedIn</a>
<a href="https://www.linkedin.com/in/fris/" rel="me noopener" target="_blank" title="LinkedIn"><i class="fa-brands fa-linkedin"></i> LinkedIn</a>
</div>
</div>
</div>
</div>
@@ -25,10 +25,9 @@
@@ -25,10 +25,9 @@
<span class="hero__rotator" aria-live="polite"><i class="fa-solid fa-chart-line"></i> Data Analyst</span>
<span class="hero__rotator" aria-live="polite"><i class="fa-solid fa-chart-line"></i> Data Analyst</span>
</p>
</p>
<p class="hero__lead">
<p class="hero__lead">
- Data analyst, systems architect, and sales leader with 10+ years of shipping across industries — from
- SOX-compliance tooling and autonomous AI agents at Amazon ICQA to a
- solo-built 15-microservice open-source creator platform, with roots in
- retail sales management at Verizon Wireless (Sales Rep → Assistant Manager).
+ Data analyst and full-stack builder with 10+ years of shipping —
+ SOX-compliance tooling and autonomous AI agents at Amazon, a solo-built
+ 15-microservice creator platform, and sales leadership at Verizon.
Open to new roles.
Open to new roles.
</p>
</p>
<div class="hero__cta">
<div class="hero__cta">
@@ -39,11 +38,9 @@
@@ -39,11 +38,9 @@
<div class="hero__chips">
<div class="hero__chips">
<span class="chip"><i class="fa-solid fa-chart-line"></i> Analytics / BI</span>
<span class="chip"><i class="fa-solid fa-chart-line"></i> Analytics / BI</span>
<span class="chip"><i class="fa-solid fa-code"></i> Full-Stack Eng</span>
<span class="chip"><i class="fa-solid fa-code"></i> Full-Stack Eng</span>
- <span class="chip"><i class="fa-solid fa-robot"></i> Autonomous Agents</span>
- <span class="chip"><i class="fa-solid fa-gear"></i> Robotics / AFM</span>
- <span class="chip"><i class="fa-solid fa-hammer"></i> Product Builder</span>
- <span class="chip"><i class="fa-solid fa-comment-dollar"></i> Sales</span>
- <span class="chip"><i class="fa-solid fa-users"></i> Management / Leadership</span>
+ <span class="chip"><i class="fa-solid fa-robot"></i> AI Agents</span>
+ <span class="chip"><i class="fa-solid fa-gear"></i> Robotics</span>
+ <span class="chip"><i class="fa-solid fa-users"></i> Sales & Leadership</span>
</div>
</div>
</div>
</div>
@@ -113,11 +110,15 @@
@@ -113,11 +110,15 @@
.hero__portrait { width: min(220px, 55vw); margin-bottom: 8px; }
.hero__portrait { width: min(220px, 55vw); margin-bottom: 8px; }
}
}
+ /* Solid color on purpose — background-clip:text + color:transparent breaks
+ when Splitting.js wraps chars in inline-block spans (text renders invisible
+ on some GPUs/viewports, e.g. Steam Deck at 1280×800). Same bug class as the
+ earlier ImpactMetrics "invisible numbers" fix. */
.hero__title {
.hero__title {
- font-size: clamp(3rem, 9vw, 6.5rem);
- background: linear-gradient(180deg, #fff 30%, #8aa0b6 100%);
- -webkit-background-clip: text; background-clip: text;
- color: transparent;
+ font-size: clamp(2.6rem, 7vw, 5.5rem);
+ color: var(--fg);
+ text-shadow: 0 2px 24px rgba(0, 0, 0, 0.35);
+ overflow-wrap: break-word;
margin-bottom: 0.4em;
margin-bottom: 0.4em;
}
}
.hero__role {
.hero__role {
@@ -217,13 +218,10 @@
@@ -217,13 +218,10 @@
const roles = [
const roles = [
`<i class="fa-solid fa-chart-line"></i> Data Analyst`,
`<i class="fa-solid fa-chart-line"></i> Data Analyst`,
`<i class="fa-solid fa-code"></i> Software Engineer`,
`<i class="fa-solid fa-code"></i> Software Engineer`,
- `<i class="fa-solid fa-robot"></i> Autonomous Agent Builder`,
+ `<i class="fa-solid fa-robot"></i> AI Agent Builder`,
`<i class="fa-solid fa-server"></i> Systems Admin`,
`<i class="fa-solid fa-server"></i> Systems Admin`,
- `<i class="fa-solid fa-cube"></i> Product Engineer`,
- `<i class="fa-brands fa-twitch"></i> Streaming Platform Builder`,
- `<i class="fa-solid fa-tree"></i> Pacific Northwest Native`,
- `<i class="fa-solid fa-comment-dollar"></i> Sales`,
- `<i class="fa-solid fa-users"></i> Management / Leadership`
+ `<i class="fa-solid fa-users"></i> Sales Leader`,
+ `<i class="fa-solid fa-tree"></i> Pacific Northwest Native`
];
];
const rot = document.querySelector(".hero__rotator");
const rot = document.querySelector(".hero__rotator");
if (rot) {
if (rot) {
@@ -1,10 +1,10 @@
@@ -1,10 +1,10 @@
---
---
const metrics = [
const metrics = [
- { num: "1,000+", label: "SOX-compliance IRDRs hand-researched at Amazon ICQA", icon: "fa-solid fa-magnifying-glass-chart" },
- { num: "15", label: "OpenVibe microservices designed, built, and shipped", icon: "fa-solid fa-cubes-stacked" },
- { num: "200K+", label: "Live game servers tracked across GameServerStats", icon: "fa-solid fa-tower-broadcast" },
- { num: "20+", label: "Amazon TamperMonkey/Python automation scripts", icon: "fa-solid fa-code" },
- { num: "10+", label: "Years running my own LLC end-to-end", icon: "fa-solid fa-building-flag" },
+ { num: "1,000+", label: "SOX-compliance IRDRs researched at Amazon", icon: "fa-solid fa-magnifying-glass-chart" },
+ { num: "15", label: "OpenVibe microservices built solo", icon: "fa-solid fa-cubes-stacked" },
+ { num: "200K+", label: "Live game servers tracked", icon: "fa-solid fa-tower-broadcast" },
+ { num: "20+", label: "Automation scripts for Amazon workflows", icon: "fa-solid fa-code" },
+ { num: "10+", label: "Years running my own LLC", icon: "fa-solid fa-building-flag" },
];
];
---
---
<section class="impact" aria-labelledby="impact-title" data-reveal>
<section class="impact" aria-labelledby="impact-title" data-reveal>
@@ -5,10 +5,10 @@
@@ -5,10 +5,10 @@
"end": "2025",
"end": "2025",
"location": "PAE2 · Arlington, WA",
"location": "PAE2 · Arlington, WA",
"bullets": [
"bullets": [
- "Own SOX-compliance IRDR (Inventory Discrepancy Research Report) workflows — hand-researched 1,000+ inventory mismatches and built the data systems that support Sarbanes-Oxley audit requirements.",
- "Built ICQA SQL and Excel reporting for inventory accuracy, discrepancy review, and defect visibility; surfaced findings in dashboards and recurring leadership reports.",
- "Authored an Anthropic-powered SQL copilot that contextually references every available data table for analysts.",
- "Built and shipped an autonomous root-cause-analysis bot that performs full end-to-end inventory research."
+ "Owned SOX-compliance IRDR workflows — researched 1,000+ inventory discrepancies and built the data systems behind Sarbanes-Oxley audit requirements.",
+ "Built SQL and Excel reporting for inventory accuracy and defect visibility; delivered dashboards and recurring leadership reports.",
+ "Created an Anthropic-powered SQL copilot that references every available data table for analysts.",
+ "Shipped an autonomous root-cause bot that performs end-to-end inventory research."
],
],
"stack": ["SQL", "Excel", "Python", "Anthropic API", "TamperMonkey", "Power Query", "SOX Compliance"],
"stack": ["SQL", "Excel", "Python", "Anthropic API", "TamperMonkey", "Power Query", "SOX Compliance"],
"featured": true,
"featured": true,
@@ -5,9 +5,9 @@
@@ -5,9 +5,9 @@
"end": "2024",
"end": "2024",
"location": "Amazon Robotics launch site",
"location": "Amazon Robotics launch site",
"bullets": [
"bullets": [
- "Monitored Amazon Robotics (ARS) automation floor during launch-stage operations — enforcing amnesty zones, clearing obstructions, and maintaining safe pod/drive flow.",
- "Investigated pod, drive unit, and workstation issues on the floor; escalated recurring defects and documented patterns to support process improvement.",
- "Coordinated amnesty events, ensured safe robot re-entry after floor interventions, and communicated status to ops leadership in real time."
+ "Monitored the Amazon Robotics floor during launch operations — enforced amnesty zones, cleared obstructions, and kept pod/drive flow safe.",
+ "Investigated pod, drive unit, and workstation issues; documented recurring defects to drive process improvement.",
+ "Coordinated amnesty events and communicated floor status to ops leadership in real time."
],
],
"stack": ["Amazon Robotics", "AFM protocols", "Floor safety", "Amnesty ops"],
"stack": ["Amazon Robotics", "AFM protocols", "Floor safety", "Amnesty ops"],
"featured": true,
"featured": true,
@@ -4,10 +4,10 @@
@@ -4,10 +4,10 @@
"start": "2020",
"start": "2020",
"end": "2023",
"end": "2023",
"bullets": [
"bullets": [
- "Consistently ranked among top performers in the district — hit and exceeded monthly quotas across smartphones, plans, accessories, device protection, financing, and Verizon Visa card offers.",
- "Promoted to Assistant Manager; mentored and coached a team of 4–8 reps through weekly 1-on-1s, floor coaching, and monthly skill workshops covering objection handling, upselling, and customer retention.",
- "Led store opens/closes, managed inventory, resolved escalated customer issues, and ran daily huddles to align the team on promos and targets.",
- "Built genuine rapport with customers — turned walk-ins into multi-line accounts and walk-outs into long-term brand advocates through consultative, needs-first selling."
+ "Ranked among the district's top performers — consistently exceeded monthly quotas across phones, plans, accessories, protection, and financing.",
+ "Promoted to Assistant Manager; coached a team of 4–8 reps through 1-on-1s, floor coaching, and skill workshops.",
+ "Ran store opens/closes, inventory, escalations, and daily huddles.",
+ "Turned walk-ins into multi-line accounts through consultative, needs-first selling."
],
],
"stack": ["Consultative Sales", "Team Leadership", "Coaching", "Retail Ops", "Quota Attainment", "Customer Retention"],
"stack": ["Consultative Sales", "Team Leadership", "Coaching", "Retail Ops", "Quota Attainment", "Customer Retention"],
"order": 3
"order": 3
@@ -4,12 +4,12 @@
@@ -4,12 +4,12 @@
"start": "2022",
"start": "2022",
"end": "2025",
"end": "2025",
"bullets": [
"bullets": [
- "Sole architect and engineer on OpenVibe — a 15-microservice open-source platform kernel: distributed event bus, OIDC/OAuth2 identity provider, capability contract registry, media object store, live-streaming ingest and restream, real-time chat, billing ledger, AI/search backbone, and realtime infrastructure. Self-hosted alternative to Twitch + YouTube + Patreon + Discord. 16 phases documented; 11+ fully implemented with cutover runbook and migration tooling complete.",
- "Built and operated HoboStreamer from scratch — a full live-streaming platform with WebRTC SFU, HLS, multi-destination RTMP restream (OBS → Twitch/YouTube/Kick simultaneously), auto-recorded VODs, clip detection, real-time chat with custom emotes, voice channels, stream analytics, comprehensive moderation tools, and an integrated browser game for viewers. Fully open source.",
- "Built HoboQuest — a browser MMORPG with a custom isomorphic game engine: wrote all game logic once in shared.js, compiled to both server and client via a custom MD5-diffing compiler. Source Engine-style lag compensation (client-side prediction + server reconciliation) drives the real-time multiplayer. Server-master.js orchestrates 4 independent game instances with distinct rulesets (PvP world, social hub, farm island, ship minigame). Custom Canvas 2D rendering engine with SAT collision detection. Open-world survival: building, combat, NPC AI, 7+ skills, inventory/crafting/bank, bus fast travel. In-game scratch card and slot casino; mobile touch and gamepad input. Integrated live into HoboStreamer — viewers watch and play in the same session.",
- "Maintained hobo.tools — central SSO and OAuth2 identity network across 14 interconnected services, including shared auth, service registry, image processing, maps, audio, and cross-domain session management."
+ "Sole architect of OpenVibe — a 15-microservice open-source platform kernel: event bus, OIDC/OAuth2 identity, media storage, live-streaming ingest/restream, real-time chat, billing ledger, and AI/search backbone. A self-hosted alternative to Twitch + YouTube + Patreon + Discord.",
+ "Built and operated HoboStreamer — a full live-streaming platform with WebRTC, HLS, multi-destination RTMP restream, auto-recorded VODs, clips, real-time chat, voice channels, and moderation tools. Fully open source.",
+ "Built HoboQuest — a browser MMORPG on a custom isomorphic engine: shared game logic compiled to both server and client, Source Engine-style lag compensation, and four live game worlds. Playable in a tab, streamed live inside HoboStreamer.",
+ "Ran hobo.tools — shared SSO/OAuth2 identity and service registry connecting 14 services."
],
],
- "stack": ["TypeScript", "Node.js", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "Lua", "C#", "React", "Vue"],
+ "stack": ["TypeScript", "Node.js", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "React", "Vue"],
"featured": true,
"featured": true,
"order": 4
"order": 4
}
}
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
---
---
title: "Amazon SQL Copilot"
title: "Amazon SQL Copilot"
category: "amazon"
category: "amazon"
-summary: "Anthropic-powered chatbot that generates SQL queries, intelligently referencing context from every available database table for ICQA data analysts."
+summary: "Anthropic-powered copilot that writes runnable, schema-aware SQL for ICQA analysts — no memorizing 200+ table names."
tech: ["Python", "Anthropic API", "SQL", "Schema Indexing", "Streamlit"]
tech: ["Python", "Anthropic API", "SQL", "Schema Indexing", "Streamlit"]
role: "Sole builder"
role: "Sole builder"
year: "2025"
year: "2025"
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
---
---
title: "Amazon Userscript Suite"
title: "Amazon Userscript Suite"
category: "amazon"
category: "amazon"
-summary: "Family of TamperMonkey userscripts that supercharge the daily ICQA + AFM workflow: IRDRTools, FCQuality Apex Ultra, FC/Prosearch, GCAHistory, FCLM Indirect, Oculus."
+summary: "Six TamperMonkey userscripts that remove daily friction from internal ICQA and AFM tooling — deep links, table enrichment, bulk actions."
tech: ["JavaScript", "TamperMonkey", "DOM APIs", "Internal Web Tools"]
tech: ["JavaScript", "TamperMonkey", "DOM APIs", "Internal Web Tools"]
role: "Sole builder"
role: "Sole builder"
year: "2024–2026"
year: "2024–2026"
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
---
---
title: "HoboQuest"
title: "HoboQuest"
category: "hobo"
category: "hobo"
-summary: "Browser MMORPG with a custom isomorphic game engine, Source Engine-style lag compensation, and 4-instance server architecture — no downloads, open a tab and play."
+summary: "Browser MMORPG on a custom-built engine with Source-style lag compensation — no downloads, open a tab and play."
tech: ["JavaScript", "Node.js", "WebSockets", "Canvas API", "Express", "isomorphic compiler", "lag compensation", "Canvas 2D physics"]
tech: ["JavaScript", "Node.js", "WebSockets", "Canvas API", "Express", "isomorphic compiler", "lag compensation", "Canvas 2D physics"]
role: "Founder / Engine & Gameplay Engineer"
role: "Founder / Engine & Gameplay Engineer"
year: "2023–Present"
year: "2023–Present"
@@ -11,30 +11,16 @@ featured: true
@@ -11,30 +11,16 @@ featured: true
order: 12
order: 12
---
---
-A persistent browser MMORPG built on a custom-engineered isomorphic game engine — no Unity, no Godot, no game framework. Everything from the physics and collision system to the multiplayer protocol was written from scratch.
+A persistent browser MMORPG built on a custom engine — no Unity, no Godot. Physics, collision, and the multiplayer protocol were all written from scratch.
-**Custom isomorphic compile pipeline.** All game logic lives in a single `shared.js` file containing conditional compilation blocks (`/*IF_SERVER*/.../*IF_END*/`). A custom `compiler.js` transforms it into `server.js` and `public/client.js` — the same physics, entity, and world state logic runs on both sides, preventing desyncs by design. MD5 change detection skips unnecessary recompiles.
+**Isomorphic engine.** All game logic lives in one shared file; a custom compiler emits both the server and client builds, so the exact same physics and world-state code runs on both sides — desyncs are prevented by design.
-**Source Engine-style lag compensation.** Real-time multiplayer uses client-side prediction with server reconciliation — the same methodology used in Counter-Strike and Valve's Source Engine. Movement and combat feel instant for the player while the server remains authoritative over all state.
+**Source Engine-style netcode.** Client-side prediction with server reconciliation (the Counter-Strike approach): movement and combat feel instant while the server stays authoritative.
-**Multi-instance server architecture.** A `server-master.js` orchestrator spawns four independent game-server processes (ports 9000–9003), each running a different map with its own ruleset:
-- Default world — PvP enabled, player building enabled
-- Central city — social hub with vendors; PvP disabled
-- Farm island — resource farming; PvP enabled, building disabled
-- Ship minigame — dedicated activity instance
+**Four live worlds.** An orchestrator runs four game instances with distinct rulesets — a PvP building world, a safe social hub, a farming island, and a ship minigame.
-**Canvas 2D rendering engine.** Built a custom Canvas 2D rendering layer (`lib/c2d.js`) with a full 2D separating-axis theorem (SAT) collision detection system, polygon/circle collision, voronoi region edge detection, and frame-accurate hit resolution — no third-party physics library.
+**Custom rendering & physics.** Canvas 2D renderer with full SAT collision detection (polygon/circle, frame-accurate hits) — no third-party physics library.
-**Open-world survival systems.** Tile-based 512×512 open world with:
-- **Building**: place and upgrade walls, doors, beds, tool cupboards, campfires, barbed wire
-- **Combat**: melee knockback, ranged pistol, arrows with ballistic gravity arc, NPC aggro with patrol/attack telegraph/hit flash
-- **Skills**: Woodcutting, Mining, Herbology, Attack, and more — persistent per player
-- **Economy**: inventory, crafting recipes, equipment slots, bank system, bus system for zone travel
+**Full survival loop.** Building, combat (melee, ranged, NPC AI), persistent skills, crafting, banking, fast travel — plus an in-game scratch-card and slots casino with server-validated state.
-**In-game casino.** Scratch card and slot machine minigames with a credit balance system, SoundManager 2 audio integration, and server-validated game state.
-
-**Cross-platform input.** Mobile touch controls overlay (tap/joystick buttons) with runtime touch detection, plus gamepad detection with a controller configuration UI. Desktop WASD + mouse.
-
-**Integrated with HoboStreamer.** The game is embedded live in the streaming platform — viewers watch and play simultaneously, with chat bridged between the game and the stream.
-
-The collaborative pixel canvas is a separate 512×512 real-time art board for territory battles and group art, also accessible from [hobo.quest](https://hobo.quest).
+**Play anywhere.** Desktop WASD + mouse, mobile touch overlay, and gamepad support. Embedded live in HoboStreamer, so viewers watch and play in the same session.
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
---
---
title: "Autonomous IRDR Root-Cause Bot"
title: "Autonomous IRDR Root-Cause Bot"
category: "amazon"
category: "amazon"
-summary: "Fully autonomous inventory root-cause-analysis agent that plans, gathers evidence, and writes a complete IRDR research report end-to-end — supporting SOX-compliant inventory accuracy."
+summary: "Autonomous agent that plans, gathers evidence, and writes complete SOX-compliant inventory research reports — hours of analysis down to seconds."
tech: ["Python", "Anthropic API", "Tool-use Agents", "TamperMonkey", "SQL", "SOX Compliance"]
tech: ["Python", "Anthropic API", "Tool-use Agents", "TamperMonkey", "SQL", "SOX Compliance"]
role: "Sole builder"
role: "Sole builder"
year: "2025"
year: "2025"
@@ -1,7 +1,7 @@
@@ -1,7 +1,7 @@
---
---
title: "OpenVibe"
title: "OpenVibe"
category: "hobo"
category: "hobo"
-summary: "Open-source creator platform kernel: 15+ microservices, distributed event bus, OIDC/OAuth2 identity, live streaming, media storage, billing ledger, AI backbone, and real-time infrastructure — sole architect and engineer across 16 documented phases."
+summary: "Open-source creator platform kernel — 15+ microservices spanning identity, live streaming, media, billing, and AI, designed and built by one person."
tech: ["Node.js", "TypeScript", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "REST APIs", "Event-Driven Architecture", "Microservices"]
tech: ["Node.js", "TypeScript", "Express", "SQLite", "PostgreSQL", "WebRTC", "WebSockets", "Redis", "OAuth2", "OIDC", "FFmpeg", "REST APIs", "Event-Driven Architecture", "Microservices"]
role: "Sole Architect & Principal Engineer"
role: "Sole Architect & Principal Engineer"
year: "2024–2026"
year: "2024–2026"
@@ -10,24 +10,16 @@ featured: true
@@ -10,24 +10,16 @@ featured: true
order: 5
order: 5
---
---
-A fully open-source, self-hosted platform kernel designed to replace the Hobo Network and serve as a production-grade alternative to the entire creator economy stack — Twitch, YouTube, Patreon, Reddit, and Discord in a single deployable monorepo. Built by one person across 16 documented phases, with cutover runbook and migration tooling complete.
+A fully open-source, self-hosted platform kernel — Twitch, YouTube, Patreon, and Discord in one deployable monorepo. Designed and built by one person across 16 documented phases.
-**Services shipped (13 microservices):**
+**What's in it:**
-- **openvibe-network** — Control plane: OIDC/OAuth2 identity provider, service registry, capability contract registry, user-module namespace API, policy engine, and host-aware routing across `auth.openvibe.network`, `api.openvibe.network`, `admin.openvibe.network`
-- **openre-stream** — RTMP ingest and multi-destination restream (OBS → Twitch / YouTube / Kick / any RTMP endpoint simultaneously)
-- **openvibe-live** — Live streaming frontend: SSR viewer surface, DVR, viewer analytics, stream presence
-- **openvibe-media** — Media object store: VODs, clips, thumbnails, stream snapshots, paste/file storage — with namespace registry, derivative pipeline, quota enforcement, and lifecycle audit log
-- **openvibe-chat** — Real-time chat: moderation, purges, bans, custom emotes, channel threading
-- **openvibe-community** — Forums, reactions, community posts, Discord-style channels, paste versioning
-- **openvibe-billing** — Credits ledger, tips, VIP status tiers, payout engine, SOX-style audit trail
-- **openvibe-ai** — AI/search backbone: autonomous content tagging, SEO generation, analyst copilot, embedding-based search
-- **openvibe-games** — Embedded browser game surface (HoboQuest integration, live viewer participation)
-- **openvibe-events** — Distributed event bus: full audit envelope (`event_id`, `trace_id`, `version`, `actor`), topic + subscription filtering, ack/retry work queue, dead-letter tables
-- **openvibe-realtime** — WebSocket hub, pub/sub presence layer, fan-out infrastructure
-- **openvibe-workers** — Task queue, scheduled jobs, background processors, queue-native pipeline
-- **openvibe-content** — Content workflows: publishing pipelines, moderation queue, product workflow seams
+- **Identity & control plane** — OIDC/OAuth2 provider, service registry, capability contracts, policy engine
+- **Live streaming** — RTMP ingest, multi-destination restream (OBS → Twitch / YouTube / Kick simultaneously), DVR, viewer analytics
+- **Media** — object store for VODs, clips, and thumbnails with derivative pipeline, quotas, and audit log
+- **Community** — real-time chat with moderation and custom emotes, forums, Discord-style channels
+- **Billing** — credits ledger, tips, VIP tiers, payout engine with SOX-style audit trail
+- **AI & search** — autonomous content tagging, SEO generation, embedding-based search
+- **Infrastructure** — distributed event bus with full audit envelope, WebSocket/pub-sub realtime layer, task queues, and shared typed-contract packages (SQLite + PostgreSQL dual-mode persistence)
-**Shared packages:** `openvibe-contracts` (typed event schemas), `openvibe-sdk` (cross-service clients), `openvibe-persistence` (SQLite + PostgreSQL dual-mode with schema parity enforcement), `openvibe-queue`, `openvibe-runtime`, `openvibe-storage`, `openvibe-redis`, `openvibe-observability`
-
-**Phase completion:** 11+ of 16 phases fully implemented. Remaining phases paused at production-readiness stage — cutover runbook written, migration tooling from HoboStreamer/hobo.tools complete, schema drift checks green. Servers brought offline in May 2026 due to funding constraints during job search. The architecture is sound and the code is all there.
+**Status:** 11+ of 16 phases fully implemented; cutover runbook and migration tooling complete. Servers offline since May 2026 during job search — the architecture is sound and the code is all there.
@@ -64,7 +64,7 @@ const pageUrl = canonical ?? new URL(Astro.url.pathname, Astro.site).toString();
@@ -64,7 +64,7 @@ const pageUrl = canonical ?? new URL(Astro.url.pathname, Astro.site).toString();
"addressCountry": "US"
"addressCountry": "US"
},
},
"sameAs": [
"sameAs": [
- "https://github.com/HoboStreamer",
+ "https://github.com/OpenVibers",
"https://www.linkedin.com/in/fris/"
"https://www.linkedin.com/in/fris/"
]
]
}
}
@@ -21,30 +21,24 @@ import ContactBlock from "../components/ContactBlock.astro";
@@ -21,30 +21,24 @@ import ContactBlock from "../components/ContactBlock.astro";
<h2 class="section-title">A versatile builder</h2>
<h2 class="section-title">A versatile builder</h2>
<p class="profile-para">
<p class="profile-para">
<i class="fa-solid fa-microchip profile-para__icon"></i>
<i class="fa-solid fa-microchip profile-para__icon"></i>
- <span>Data analyst and systems architect with 10+ years of shipping — from
- <strong>SOX-compliance tooling at Amazon ICQA</strong> to autonomous
- AI research agents to a <strong>15-microservice open-source platform
- kernel</strong>, all built and operated solo. Strong at translating
- operational context into production-grade tooling, stakeholder-ready
- analysis, and systems that actually scale.</span>
+ <span>Data analyst and systems architect with 10+ years of shipping —
+ from <strong>SOX-compliance tooling at Amazon ICQA</strong> to
+ autonomous AI agents to a <strong>15-microservice open-source
+ platform</strong>, built and operated solo. I turn operational
+ context into tools and analysis stakeholders can act on.</span>
</p>
</p>
<p class="profile-para">
<p class="profile-para">
<i class="fa-solid fa-store profile-para__icon"></i>
<i class="fa-solid fa-store profile-para__icon"></i>
- <span>Before the tech career, I cut my teeth in retail sales at
- <strong>Verizon Wireless</strong> — first as a Sales Representative,
- then promoted to <strong>Assistant Manager</strong>. Quota-driven
- environment, high customer volume, team coaching, and the kind of
- communication pressure that either breaks you or makes you sharp.
- It made me sharp. That foundation — reading people, closing clearly,
- handling objections — still shows up every time I present data or
- walk a stakeholder through a decision.</span>
+ <span>Before tech, I sold at <strong>Verizon Wireless</strong> —
+ promoted from Sales Rep to <strong>Assistant Manager</strong> in a
+ quota-driven store. Reading people, handling objections, and closing
+ clearly still shape how I present data and decisions.</span>
</p>
</p>
<p class="profile-para">
<p class="profile-para">
<i class="fa-solid fa-screwdriver-wrench profile-para__icon"></i>
<i class="fa-solid fa-screwdriver-wrench profile-para__icon"></i>
- <span>Tech-geek since 12, building computers and chasing bleeding-edge
- hardware. A decade running my own LLC, ten years tracking 200,000+
- live game servers, and 1,000+ IRDRs personally researched at Amazon.
- I ship fast and keep what works.</span>
+ <span>Building computers since age 12. A decade running my own LLC,
+ ten years tracking 200,000+ live game servers, 1,000+ IRDRs
+ researched at Amazon. I ship fast and keep what works.</span>
</p>
</p>
</div>
</div>
<aside class="card profile__facts" data-reveal>
<aside class="card profile__facts" data-reveal>
@@ -54,7 +48,7 @@ import ContactBlock from "../components/ContactBlock.astro";
@@ -54,7 +48,7 @@ import ContactBlock from "../components/ContactBlock.astro";
<li><strong><i class="fa-solid fa-briefcase"></i> Open to</strong>Hybrid · Remote · On-site PNW</li>
<li><strong><i class="fa-solid fa-briefcase"></i> Open to</strong>Hybrid · Remote · On-site PNW</li>
<li><strong><i class="fa-solid fa-building-columns"></i> Founder</strong>GameServerStats, LLC</li>
<li><strong><i class="fa-solid fa-building-columns"></i> Founder</strong>GameServerStats, LLC</li>
<li><strong><i class="fa-solid fa-laptop-code"></i> Last role</strong>Amazon · Data Analyst (ICQA)</li>
<li><strong><i class="fa-solid fa-laptop-code"></i> Last role</strong>Amazon · Data Analyst (ICQA)</li>
- <li class="profile__facts-seeking"><strong><i class="fa-solid fa-magnifying-glass"></i> Seeking</strong><span class="seek-groups"><span class="seek-group"><i class="fa-solid fa-chart-line"></i> Analyst · Data Engineer · BI</span><span class="seek-group"><i class="fa-solid fa-server"></i> IT · Systems · Robotics · Technician</span><span class="seek-group"><i class="fa-solid fa-people-group"></i> Sales · Leadership · Management</span><span class="seek-group"><i class="fa-solid fa-hammer"></i> Builder · Architect · Maker</span></span></li>
+ <li class="profile__facts-seeking"><strong><i class="fa-solid fa-magnifying-glass"></i> Seeking</strong><span class="seek-groups"><span class="seek-group"><i class="fa-solid fa-chart-line"></i> Analyst · Data Engineer · BI</span><span class="seek-group"><i class="fa-solid fa-server"></i> IT · Systems · Robotics</span><span class="seek-group"><i class="fa-solid fa-people-group"></i> Sales · Leadership · Management</span></span></li>
</ul>
</ul>
</aside>
</aside>
</div>
</div>