content: publish evidence-led portfolio copy

This commit is contained in:
2026-09-04 20:22:45 +03:00
parent 528099c74e
commit 38fb9e4330
8 changed files with 236 additions and 85 deletions

View File

@@ -2,7 +2,9 @@
"identity": { "identity": {
"name": "Luca Pîslaru", "name": "Luca Pîslaru",
"role": "System Administrator & Full-Stack Developer", "role": "System Administrator & Full-Stack Developer",
"valueProposition": "Building and operating reliable systems and software." "valueProposition": "I build full-stack software and operate the Linux systems behind it, from offline-first applications to access-controlled self-hosted services and careful data migrations.",
"location": { "city": "Bucharest", "country": "Romania", "countryCode": "RO" },
"availability": "Based in Bucharest and open to on-site, hybrid, or remote roles locally, as well as international remote opportunities."
}, },
"navigation": [ "navigation": [
{ "id": "start", "label": "Start" }, { "id": "start", "label": "Start" },
@@ -15,81 +17,98 @@
{ "id": "contact", "label": "Contact" } { "id": "contact", "label": "Contact" }
], ],
"profile": { "profile": {
"introduction": "I work across Linux infrastructure and full-stack delivery, pairing practical operations with software built to be understood, maintained, and improved over time.", "introduction": "I’m an early-career technologist working across Linux administration and end-to-end application development. I value systems that can be understood, maintained, secured, and recovered: clear access boundaries, deliberate deployments, useful documentation, and software refined through real use.",
"screeningFacts": [ "screeningFacts": [
{ "label": "Education", "value": "Bachelor's degree in Business Management IT, Romanian-American University, 2023-2026" }, { "label": "Education", "value": "Bachelor's degree in Business Management IT, Romanian-American University, 2023-2026" },
{ "label": "Languages", "value": "Romanian (native), English (C2), French (A1)" }, { "label": "Languages", "value": "Romanian (native), English (C2), French (A1)" },
{ "label": "Certification", "value": "PCEP - Certified Entry-Level Python Programmer, 2022" } { "label": "Certifications", "value": "Advanced Digital Competencies Certificate, 2025; PCEP - Certified Entry-Level Python Programmer, 2022" }
] ]
}, },
"experience": [ "experience": [
{ {
"id": "mihai-razus-pfa", "id": "trupa-lui-razus",
"role": "Full-Stack Developer & DevOps collaborator", "role": "Full-Stack Developer & Hosting Administrator (Collaborator)",
"organization": "Mihai Razus PFA", "organization": "Trupa lui Răzuș",
"dates": "Aug 2025 - Present", "dates": "August 2025-Present",
"location": "Bucharest, Romania",
"highlights": [ "highlights": [
"Contribute to full-stack delivery across application and deployment work.", "Built the theatre group's first owned web presence, replacing a social-media-only discovery path with an indexable website and direct contact options.",
"Support practical DevOps collaboration alongside software development." "Developed a custom Vue frontend and a headless WordPress publishing workflow that lets an editor publish news and promotional content without changing code.",
"Configured role-separated CMS access and restricted administrative routes and endpoints while keeping exact access rules private.",
"Manage the domain, cPanel hosting, DNS/TLS configuration, custom .htaccess behavior, and controlled Git/SSH deployments with verification and rollback."
] ]
}, },
{ {
"id": "camera-consultantilor-fiscali", "id": "camera-consultantilor-fiscali",
"role": "Green Innovation Intern", "role": "Green Innovation Intern",
"organization": "Camera Consultanților Fiscali", "organization": "Camera Consultanților Fiscali",
"dates": "Jun 2025 - Aug 2025", "dates": "June 2025-August 2025",
"location": "Bucharest, Romania",
"highlights": [ "highlights": [
"Contributed to green innovation work in an internship setting.", "Contributed to eco-innovation work within the INOVATIV 2030 program.",
"Applied structured problem-solving and digital skills to assigned work." "Developed and presented sustainable-technology business models."
] ]
}, },
{ {
"id": "isense-solutions", "id": "isense-solutions",
"role": "Data Science Intern", "role": "Data Science Intern",
"organization": "iSense Solutions", "organization": "iSense Solutions",
"dates": "Jun 2025 - Aug 2025", "dates": "June 2025-August 2025",
"location": "Bucharest, Romania",
"highlights": [ "highlights": [
"Contributed to data science work in an internship setting.", "Processed and analyzed datasets using SPSS and Microsoft Excel.",
"Applied analytical and programming skills to assigned tasks." "Prepared quantitative reports supporting business-development decisions."
] ]
} }
], ],
"systemsOperations": { "systemsOperations": {
"id": "professional-systems-operations", "id": "professional-systems-operations",
"title": "Professional systems operations", "title": "Professional systems operations",
"summary": "Professional work that connects application delivery with the Linux environments and deployment practices that support it.", "summary": "Application delivery and hosting work with explicit boundaries between public content, authenticated administration, controlled releases, and recovery.",
"evidence": [ "evidence": [
"Linux server administration and service management.", "Public Vue frontend backed by a role-separated headless WordPress editing workflow.",
"Docker and Nginx deployment work, including TLS-aware service delivery.", "Private source control followed by a deliberate SSH deployment, post-release verification, and Git-based rollback path.",
"Operational troubleshooting alongside full-stack development." "Domain, DNS, TLS, cPanel, and restricted-route administration performed alongside application development."
] ]
}, },
"infrastructure": { "infrastructure": {
"id": "self-hosted-platform-lifecycle", "id": "self-hosted-platform-lifecycle",
"title": "Secure self-hosted platform and lifecycle", "title": "Secure self-hosted platform and lifecycle",
"summary": "A personal Linux platform operated as a compact systems case study: secure ingress, deliberate access controls, service lifecycle discipline, and routine maintenance.", "summary": "A self-hosted Linux environment operated as a long-term systems practice: traffic enters through a controlled edge, selected services receive an additional identity boundary, changes are staged deliberately, and important data has local and offsite recovery paths.",
"bands": [ "bands": [
{ {
"id": "ingress-and-tls", "id": "ingress-and-tls",
"title": "Ingress and TLS", "title": "Ingress and TLS",
"summary": "Routes services through Nginx with certificate issuance and renewal managed through Let's Encrypt and Certbot; exposed entry points receive fail2ban protection." "summary": "Route services through Nginx, manage DNS and certificate lifecycle, and use Fail2Ban at exposed entry points."
}, },
{ {
"id": "identity-and-access", "id": "identity-and-access",
"title": "Identity and access", "title": "Identity and access",
"summary": "Uses an authentication layer for protected services, with passkeys, two-factor authentication, and recovery considered without publishing policy details." "summary": "Place selected services behind Authelia and constrain administrative, filesystem, and container access without publishing the underlying policy."
}, },
{ {
"id": "service-lifecycle", "id": "service-lifecycle",
"title": "Service lifecycle", "title": "Service lifecycle",
"summary": "Deploys containerized services, manages updates, and treats service enrollment and retirement as part of ongoing maintenance." "summary": "Deploy and update services with Docker Compose, including a full platform migration from a Raspberry Pi to a dedicated Linux server."
}, },
{ {
"id": "operations-and-maintenance", "id": "operations-and-recovery",
"title": "Operations", "title": "Operations and recovery",
"summary": "Investigates service issues and performs routine maintenance while keeping the platform understandable and manageable." "summary": "Use Btrfs storage, a pre-upgrade backup script, rsync-based copies, and an offsite backup for important data."
} }
] ],
"featuredOperation": {
"title": "Migrating a stateful Nextcloud service",
"context": "The existing standalone deployment contained much more than files: contacts, calendars, tasks, and collaboration data all had to survive a move to Nextcloud All-in-One.",
"actions": [
"Aligned application versions and updated installed applications before migration.",
"Backed up the source deployment and prepared rollback checkpoints.",
"Converted the existing MySQL database to PostgreSQL before the All-in-One migration.",
"Exported and imported application state, transferred the data directory, corrected ownership and permissions, and preserved required instance configuration.",
"Validated access to the expected files and services before relying on the new deployment."
],
"outcome": "The migration completed without data loss. The All-in-One deployment has remained Luca's active Nextcloud instance for roughly a year, including storage, synchronization, contacts, calendars, tasks, and Nextcloud Talk."
}
}, },
"projects": { "projects": {
"selected": [ "selected": [
@@ -98,29 +117,34 @@
"title": "Private self-hosted medication tracker", "title": "Private self-hosted medication tracker",
"visibility": "private", "visibility": "private",
"maturity": "active", "maturity": "active",
"summary": "A private multi-device medication system for schedules, stock awareness, adherence logging, and shared administration.", "summary": "A solo-built, self-hosted medication-tracking system with an offline-first Flutter client, TypeScript API, PostgreSQL database, and Vue administration interface.",
"problem": "Medication routines need a dependable way to handle scheduled and as-needed entries across devices.", "problem": "A private medication routine needed dependable offline tracking, reminders, history, and synchronization without turning personal data into a public product.",
"contribution": "Designed and built the private client, server, synchronization, and administrative workflow.", "contribution": "Independently designed, implemented, deployed, and operated the complete mobile app, API, administration interface, database, synchronization model, and self-hosted environment.",
"technicalDecisions": [ "technicalDecisions": [
"Supports profiles, scheduled and as-needed medication, stock tracking, notifications, and logged activity.", "Use Flutter, Riverpod, and Drift/SQLite so the mobile workflow remains useful offline and can queue changes before pulling server updates.",
"Keeps a server component and administrative interface alongside the multi-device experience." "Use Fastify, TypeScript, Drizzle, and PostgreSQL for the API and synchronized data model, alongside a Vue administration interface.",
"Apply authentication, refresh-token, TOTP, backup-code, biometric-lock, and database access-boundary mechanisms while continuing to harden the system through tests and audit findings.",
"Run the containerized service behind Nginx/TLS and an additional Authelia access layer.",
"Support schedules, as-needed entries, stock tracking, dose history, reminders, background sync, exports, analytics, an Android widget, and English/Romanian localization."
], ],
"outcome": "The product remains private; this portfolio presents its scope without a public link or media.", "outcome": "The system has supported Luca's private medication tracking consistently for several months. Real use exposed Android reminder failures; investigation restored dependable delivery, and current work is replacing the remaining recurring behavior with occurrence-specific reconciliation to address duplicate and stale reminders. The project works for its current private use but is not presented as ready for production.",
"assetIds": [] "assetIds": []
}, },
{ {
"id": "footy-tracker", "id": "footy-tracker",
"title": "Footy Tracker", "title": "Footy Tracker",
"visibility": "public", "visibility": "public",
"maturity": "work-in-progress", "maturity": "active",
"summary": "A work-in-progress computer-vision pipeline for football footage.", "summary": "A two-person university computer-vision project that processed previously unseen football footage and generated an annotated output video.",
"problem": "Football footage needs a repeatable way to detect and follow players over time.", "problem": "Build a time-constrained demonstration that could identify football players, referees, and the ball in match footage and explore jersey-number recognition.",
"contribution": "Built the player detection and tracking pipeline and continue to explore jersey recognition.", "contribution": "Led the project's data and machine-learning work: dataset acquisition and conversion, YOLO configuration and detector training, jersey-classifier design, experimentation, checkpointing, evaluation, and demo integration. A collaborator created the initial repository structure and video-processing pipeline.",
"technicalDecisions": [ "technicalDecisions": [
"Separates player detection and tracking from the ongoing jersey-recognition work.", "Convert SoccerNet tracking data into a reproducible YOLO training layout and keep training behavior configuration-driven.",
"Keeps the pipeline focused on footage analysis rather than claiming production readiness." "Compare pretrained ResNet, MobileNet, and EfficientNet variants for jersey classification.",
"Add class weighting, checkpoint/resume, focal loss, mixup/cutmix, label smoothing, and early stopping as controlled experiments rather than assuming each technique would improve the model.",
"Prefer the simpler ResNet34 configuration after an eight-hour ablation set showed that aggressive augmentation increased overfitting and reduced validation performance."
], ],
"outcome": "Player detection and tracking are the current focus; jersey recognition remains ongoing work.", "outcome": "The submitted demo processed previously unseen, compressed broadcast footage and retained strong player detection. Measured validation results were 0.942 mAP@0.5 for players, 0.726 for referees, and 0.198 for the ball. The best jersey classifier reached approximately 0.778 validation accuracy, while integrated jersey labels remained inconsistent. The result is a working academic demonstration, not a production tracking system.",
"url": "https://github.com/LucaPisl/footy-tracker", "url": "https://github.com/LucaPisl/footy-tracker",
"assetIds": [] "assetIds": []
}, },
@@ -129,14 +153,15 @@
"title": "Opinion Dynamics Simulation", "title": "Opinion Dynamics Simulation",
"visibility": "public", "visibility": "public",
"maturity": "experimental", "maturity": "experimental",
"summary": "An experimental agent-based simulation for exploring how group opinions change.", "summary": "An agent-based research experiment for observing how local interactions, network structure, and cascade conditions shape group-level opinion patterns.",
"problem": "Explore how local interactions can produce different group-level opinion patterns.", "problem": "Explore how simplified local rules can produce polarization, rapid cascades, and uneven final distributions.",
"contribution": "Built the experimental model and the result views used to inspect its runs.", "contribution": "Built the simulation and analysis views used to inspect modeled runs and compare changes over time.",
"technicalDecisions": [ "technicalDecisions": [
"Treats the work as an experimental model rather than a predictive product.", "Keep the work explicitly experimental rather than presenting it as a predictive model of real people.",
"Uses structured result views to compare changing variables and observed trends." "Expose polarization, cascade thresholds, graph degree, revolt frequency, and final distributions as inspectable outputs.",
"Document performance and network-formation limitations alongside the results."
], ],
"outcome": "The project is presented as research exploration, with conclusions limited to the modeled scenarios.", "outcome": "The project produced useful exploratory visualizations and made the model's assumptions and limitations visible; conclusions remain limited to the simulated scenarios.",
"url": "https://github.com/LucaPisl/opinion-dynamics-simulation", "url": "https://github.com/LucaPisl/opinion-dynamics-simulation",
"assetIds": [] "assetIds": []
} }
@@ -147,13 +172,13 @@
"title": "Private game prototype", "title": "Private game prototype",
"visibility": "private", "visibility": "private",
"maturity": "prototype", "maturity": "prototype",
"summary": "A private three-week prototype focused on testing a compact gameplay loop.", "summary": "A private three-week prototype built to test a compact gameplay loop under a deliberately small scope.",
"problem": "Test a game concept quickly while keeping the scope deliberately small.", "problem": "Test a game concept quickly while keeping the scope deliberately small.",
"contribution": "Built and iterated on the prototype during a three-week development window.", "contribution": "Built and iterated on the prototype during a three-week development window.",
"technicalDecisions": [ "technicalDecisions": [
"Focused the work on a short gameplay loop rather than a full production release." "Focused the work on a short gameplay loop rather than a full production release."
], ],
"outcome": "The prototype remains private and is described without a public link or media.", "outcome": "No real title, link, video, or identifying media.",
"assetIds": [] "assetIds": []
}, },
{ {
@@ -161,13 +186,13 @@
"title": "Private healthcare platform", "title": "Private healthcare platform",
"visibility": "private", "visibility": "private",
"maturity": "active", "maturity": "active",
"summary": "A private web platform presented here only at a high level.", "summary": "A confidentiality-safe summary of Luca's web-interface contribution.",
"problem": "A healthcare workflow needed a focused web-based interface.", "problem": "A healthcare workflow needed a focused web-based interface.",
"contribution": "Contributed to the private platform's web experience.", "contribution": "Contributed to the private platform's web-interface experience.",
"technicalDecisions": [ "technicalDecisions": [
"Keeps product details and any identifying interface data outside the public portfolio." "Keeps product details and any identifying interface data outside the public portfolio."
], ],
"outcome": "The platform remains private and has no public link or media.", "outcome": "No product identity, private link, screenshot, or health data.",
"assetIds": [] "assetIds": []
}, },
{ {
@@ -175,13 +200,11 @@
"title": "Currency Converter", "title": "Currency Converter",
"visibility": "public", "visibility": "public",
"maturity": "learning", "maturity": "learning",
"summary": "A modest .NET and Avalonia learning project.", "summary": "A scoped .NET/Avalonia desktop project integrating external exchange-rate data and practicing typed UI development.",
"problem": "Practice a focused desktop workflow for currency conversion.", "problem": "Practice a focused desktop workflow for currency conversion with external exchange-rate data.",
"contribution": "Implemented the application as a scoped learning exercise.", "contribution": "Implemented the application as a scoped learning exercise.",
"technicalDecisions": [ "technicalDecisions": ["Uses .NET and Avalonia to explore a typed desktop interface."],
"Uses .NET and Avalonia to explore a typed desktop interface." "outcome": "A scoped project for developing desktop application fundamentals.",
],
"outcome": "A deliberately modest project for developing desktop application fundamentals.",
"url": "https://github.com/LucaPisl/CurrencyConverterAvalonia", "url": "https://github.com/LucaPisl/CurrencyConverterAvalonia",
"assetIds": [] "assetIds": []
}, },
@@ -190,13 +213,13 @@
"title": "Linux Modding Guide", "title": "Linux Modding Guide",
"visibility": "public", "visibility": "public",
"maturity": "active", "maturity": "active",
"summary": "Community documentation that shares Linux troubleshooting and technical writing practice.", "summary": "Ongoing community documentation focused on practical Linux troubleshooting and reusable technical explanations.",
"problem": "Useful Linux troubleshooting knowledge benefits from clear, reusable documentation.", "problem": "Useful Linux troubleshooting knowledge benefits from clear, reusable documentation.",
"contribution": "Wrote and maintained community-facing technical guidance.", "contribution": "Wrote and maintained community-facing technical guidance.",
"technicalDecisions": [ "technicalDecisions": [
"Favors practical explanations that help readers understand and resolve issues." "Favors practical explanations that help readers understand and resolve issues."
], ],
"outcome": "An ongoing example of technical writing without relying on popularity counts.", "outcome": "An ongoing example of practical technical writing.",
"url": "https://github.com/LucaPisl/LinuxModdingGuide", "url": "https://github.com/LucaPisl/LinuxModdingGuide",
"assetIds": [] "assetIds": []
} }
@@ -204,35 +227,59 @@
}, },
"toolkit": [ "toolkit": [
{ {
"title": "Systems & delivery", "title": "Systems and delivery",
"technologies": [ "technologies": [
{ "name": "Linux" }, { "name": "Linux" },
{ "name": "Docker" }, { "name": "Docker" },
{ "name": "Nginx" }, { "name": "Nginx" },
{ "name": "Proxmox / KVM" }, { "name": "cPanel" },
{ "name": "DNS" },
{ "name": "TLS" },
{ "name": "Authelia" },
{ "name": "Fail2Ban" },
{ "name": "Btrfs" },
{ "name": "rsync" },
{ "name": "Nextcloud" },
{ "name": "Proxmox/KVM" },
{ "name": "Git" } { "name": "Git" }
] ]
}, },
{ {
"title": "Software & data", "title": "Applications",
"technologies": [ "technologies": [
{ "name": "Python" }, { "name": "Python" },
{ "name": "TypeScript/JavaScript" },
{ "name": "Vue.js" }, { "name": "Vue.js" },
{ "name": "JavaScript" }, { "name": "Flutter/Dart" },
{ "name": "C# / .NET" }, { "name": "Fastify" },
{ "name": "Flutter / Dart" }, { "name": "C#/.NET" },
{ "name": "PostgreSQL / SQL" }, { "name": "Java" },
{ "name": "Headless WordPress" } { "name": "Headless WordPress" }
] ]
}, },
{ {
"title": "Additional experience", "title": "Data and storage",
"technologies": [ "technologies": [
{ "name": "Java" } { "name": "PostgreSQL" },
{ "name": "SQL" },
{ "name": "Drift/SQLite" },
{ "name": "Drizzle ORM" },
{ "name": "SPSS" },
{ "name": "Microsoft Excel" }
]
},
{
"title": "Machine learning",
"technologies": [
{ "name": "PyTorch" },
{ "name": "Ultralytics/YOLO" },
{ "name": "OpenCV" },
{ "name": "SoccerNet" }
] ]
} }
], ],
"contact": { "contact": {
"introduction": "I’m open to System Administrator and Full-Stack Developer opportunities in Bucharest, including on-site, hybrid, and remote work, as well as international remote roles.",
"email": "luca.pislaru@gmail.com", "email": "luca.pislaru@gmail.com",
"social": [ "social": [
{ "service": "GitHub", "url": "https://github.com/LucaPisl" }, { "service": "GitHub", "url": "https://github.com/LucaPisl" },
@@ -243,7 +290,7 @@
}, },
"metadata": { "metadata": {
"title": "Luca Pîslaru — System Administrator & Full-Stack Developer", "title": "Luca Pîslaru — System Administrator & Full-Stack Developer",
"description": "Portfolio of Luca Pîslaru, a System Administrator & Full-Stack Developer building and operating reliable systems and software." "description": "Portfolio of Luca Pîslaru, a Bucharest-based System Administrator and Full-Stack Developer working across Linux infrastructure and end-to-end software delivery."
}, },
"soundtrack": { "soundtrack": {
"intro": { "intro": {

View File

@@ -6,20 +6,22 @@ This checklist records the Phase 1 public projection. Every item is either inclu
| --- | --- | --- | | --- | --- | --- |
| Identity | Luca Pîslaru | approved | | Identity | Luca Pîslaru | approved |
| Identity | System Administrator & Full-Stack Developer | approved | | Identity | System Administrator & Full-Stack Developer | approved |
| Identity | Building and operating reliable systems and software | approved | | Identity | Bucharest, Romania | approved |
| Identity | On-site, hybrid, and remote availability | approved |
| Profile | Education | approved | | Profile | Education | approved |
| Profile | Languages | approved | | Profile | Languages | approved |
| Profile | PCEP — Certified Entry-Level Python Programmer | approved | | Profile | Advanced Digital Competencies Certificate and PCEP - Certified Entry-Level Python Programmer | approved |
| Experience | Full-Stack Developer & DevOps collaborator at Mihai Razus PFA | approved | | Experience | Full-Stack Developer & Hosting Administrator (Collaborator) at Trupa lui Răzuș | approved |
| Experience | Green Innovation Intern at Camera Consultanților Fiscali | approved | | Experience | Green Innovation Intern at Camera Consultanților Fiscali | approved |
| Experience | Data Science Intern at iSense Solutions | approved | | Experience | Data Science Intern at iSense Solutions | approved |
| Systems operations | Professional systems operations | approved | | Systems operations | Professional systems operations | approved |
| Infrastructure | Ingress and TLS | approved | | Infrastructure | Ingress and TLS | approved |
| Infrastructure | Identity and access | approved | | Infrastructure | Identity and access | approved |
| Infrastructure | Service lifecycle | approved | | Infrastructure | Service lifecycle | approved |
| Infrastructure | Operations | approved | | Infrastructure | Nextcloud migration completed without data loss and active for roughly a year | approved |
| Selected build | Private self-hosted medication tracker | approved | | Infrastructure | Operations and recovery | approved |
| Selected build | Footy Tracker | approved | | Selected build | Private self-hosted medication tracker - private, active, and not ready for production | approved |
| Selected build | Footy Tracker collaboration and measured detector results | approved |
| Selected build | Opinion Dynamics Simulation | approved | | Selected build | Opinion Dynamics Simulation | approved |
| Additional work | Private game prototype | approved | | Additional work | Private game prototype | approved |
| Additional work | Private healthcare platform | approved | | Additional work | Private healthcare platform | approved |
@@ -47,4 +49,5 @@ This checklist records the Phase 1 public projection. Every item is either inclu
| Metadata | Page description | approved | | Metadata | Page description | approved |
| Metadata | Social preview asset | omit | | Metadata | Social preview asset | omit |
| Assets | Public asset records | omit | | Assets | Public asset records | omit |
| Assets | Project, toolkit, and metadata asset references | omit | | Assets | Project and social media | pending approval |
| Assets | Toolkit and metadata asset references | omit |

View File

@@ -116,6 +116,7 @@ const projectSchema = z.object({
url: httpUrl.optional(), url: httpUrl.optional(),
assetIds: z.array(opaqueId), assetIds: z.array(opaqueId),
media: z.array(mediaPresentationSchema).optional(), media: z.array(mediaPresentationSchema).optional(),
mediaLayout: z.enum(['single', 'portrait-strip', 'chart-pair']).optional(),
}).strict().superRefine((project, context) => { }).strict().superRefine((project, context) => {
if (project.visibility === 'private' && project.url !== undefined) { if (project.visibility === 'private' && project.url !== undefined) {
context.addIssue({ code: 'custom', message: 'A private project may not include a url.', path: ['url'] }); context.addIssue({ code: 'custom', message: 'A private project may not include a url.', path: ['url'] });
@@ -138,6 +139,13 @@ const projectSchema = z.object({
}); });
} }
} }
if (project.mediaLayout !== undefined && (project.media === undefined || project.media.length === 0)) {
context.addIssue({
code: 'custom',
message: 'Project mediaLayout requires at least one media presentation.',
path: ['mediaLayout'],
});
}
}); });
const portfolioContentSchema = z.object({ const portfolioContentSchema = z.object({
@@ -145,6 +153,12 @@ const portfolioContentSchema = z.object({
name: z.literal('Luca Pîslaru'), name: z.literal('Luca Pîslaru'),
role: z.literal('System Administrator & Full-Stack Developer'), role: z.literal('System Administrator & Full-Stack Developer'),
valueProposition: visibleString, valueProposition: visibleString,
location: z.object({
city: visibleString,
country: visibleString,
countryCode: z.string().regex(/^[A-Z]{2}$/, 'Country code must be a two-letter uppercase code.'),
}).strict(),
availability: visibleString,
}).strict(), }).strict(),
navigation: z.array(navigationItemSchema).length(8), navigation: z.array(navigationItemSchema).length(8),
profile: z.object({ profile: z.object({
@@ -170,6 +184,12 @@ const portfolioContentSchema = z.object({
title: z.literal('Secure self-hosted platform and lifecycle'), title: z.literal('Secure self-hosted platform and lifecycle'),
summary: visibleString, summary: visibleString,
bands: z.array(z.object({ id: opaqueId, title: visibleString, summary: visibleString }).strict()).length(4), bands: z.array(z.object({ id: opaqueId, title: visibleString, summary: visibleString }).strict()).length(4),
featuredOperation: z.object({
title: visibleString,
context: visibleString,
actions: z.array(visibleString).min(3).max(6),
outcome: visibleString,
}).strict(),
}).strict(), }).strict(),
projects: z.object({ projects: z.object({
selected: z.array(projectSchema).length(3), selected: z.array(projectSchema).length(3),
@@ -180,6 +200,7 @@ const portfolioContentSchema = z.object({
technologies: z.array(z.object({ name: visibleString, assetId: opaqueId.optional() }).strict()).min(1), technologies: z.array(z.object({ name: visibleString, assetId: opaqueId.optional() }).strict()).min(1),
}).strict()).min(1), }).strict()).min(1),
contact: z.object({ contact: z.object({
introduction: visibleString,
email: z.email(), email: z.email(),
social: z.array(z.object({ social: z.array(z.object({
service: z.enum(['GitHub', 'GitLab', 'LinkedIn']), service: z.enum(['GitHub', 'GitLab', 'LinkedIn']),

View File

@@ -20,6 +20,7 @@ export type SectionLayout =
export type ProjectVisibility = 'public' | 'private'; export type ProjectVisibility = 'public' | 'private';
export type ProjectMaturity = 'learning' | 'experimental' | 'prototype' | 'work-in-progress' | 'active'; export type ProjectMaturity = 'learning' | 'experimental' | 'prototype' | 'work-in-progress' | 'active';
export type ProjectMediaLayout = 'single' | 'portrait-strip' | 'chart-pair';
export interface MediaPresentationBase { export interface MediaPresentationBase {
assetId: string; assetId: string;
@@ -84,11 +85,19 @@ export interface InfrastructureEvidenceBand {
summary: string; summary: string;
} }
export interface FeaturedOperation {
title: string;
context: string;
actions: readonly string[];
outcome: string;
}
export interface InfrastructureCaseStudy { export interface InfrastructureCaseStudy {
id: string; id: string;
title: string; title: string;
summary: string; summary: string;
bands: readonly InfrastructureEvidenceBand[]; bands: readonly InfrastructureEvidenceBand[];
featuredOperation: FeaturedOperation;
} }
export interface PublicProject { export interface PublicProject {
@@ -104,6 +113,7 @@ export interface PublicProject {
url?: string; url?: string;
assetIds: readonly string[]; assetIds: readonly string[];
media?: readonly MediaPresentation[]; media?: readonly MediaPresentation[];
mediaLayout?: ProjectMediaLayout;
} }
export interface ToolkitTechnology { export interface ToolkitTechnology {
@@ -122,6 +132,7 @@ export interface ContactSocialLink {
} }
export interface ContactContent { export interface ContactContent {
introduction: string;
email: string; email: string;
social: readonly ContactSocialLink[]; social: readonly ContactSocialLink[];
cvMessage: string; cvMessage: string;
@@ -150,6 +161,8 @@ export interface PortfolioContent {
name: 'Luca Pîslaru'; name: 'Luca Pîslaru';
role: 'System Administrator & Full-Stack Developer'; role: 'System Administrator & Full-Stack Developer';
valueProposition: string; valueProposition: string;
location: { city: string; country: string; countryCode: string };
availability: string;
}; };
navigation: ReadonlyArray<{ id: SectionId; label: string }>; navigation: ReadonlyArray<{ id: SectionId; label: string }>;
profile: { introduction: string; screeningFacts: ReadonlyArray<{ label: string; value: string }> }; profile: { introduction: string; screeningFacts: ReadonlyArray<{ label: string; value: string }> };

View File

@@ -2,7 +2,9 @@
"identity": { "identity": {
"name": "Luca Pîslaru", "name": "Luca Pîslaru",
"role": "System Administrator & Full-Stack Developer", "role": "System Administrator & Full-Stack Developer",
"valueProposition": "Building and operating reliable systems and software." "valueProposition": "Building and operating reliable systems and software.",
"location": { "city": "Bucharest", "country": "Romania", "countryCode": "RO" },
"availability": "Based in Bucharest and open to remote roles."
}, },
"navigation": [ "navigation": [
{ "id": "start", "label": "Start" }, { "id": "start", "label": "Start" },
@@ -41,7 +43,13 @@
{ "id": "identity-access", "title": "Identity and access", "summary": "Protected service access." }, { "id": "identity-access", "title": "Identity and access", "summary": "Protected service access." },
{ "id": "service-lifecycle", "title": "Service lifecycle", "summary": "Maintained representative services." }, { "id": "service-lifecycle", "title": "Service lifecycle", "summary": "Maintained representative services." },
{ "id": "operations", "title": "Operations", "summary": "Resolved routine service issues." } { "id": "operations", "title": "Operations", "summary": "Resolved routine service issues." }
] ],
"featuredOperation": {
"title": "Synthetic stateful service migration",
"context": "A representative migration preserved essential application state.",
"actions": ["Prepared versions.", "Created recovery checkpoints.", "Validated the new service."],
"outcome": "The synthetic migration retained expected service data."
}
}, },
"projects": { "projects": {
"selected": [ "selected": [
@@ -55,7 +63,8 @@
"contribution": "Designed and maintained the application.", "contribution": "Designed and maintained the application.",
"technicalDecisions": ["Used a synchronised client and server design."], "technicalDecisions": ["Used a synchronised client and server design."],
"outcome": "The work remains private.", "outcome": "The work remains private.",
"assetIds": ["selected-build-one"] "assetIds": ["selected-build-one"],
"media": [{ "kind": "image", "assetId": "selected-build-one", "alt": "Synthetic project evidence." }]
}, },
{ {
"id": "public-build-two", "id": "public-build-two",
@@ -101,6 +110,7 @@
"technologies": [{ "name": "Linux", "assetId": "toolkit-linux" }] "technologies": [{ "name": "Linux", "assetId": "toolkit-linux" }]
}], }],
"contact": { "contact": {
"introduction": "Open to System Administrator and Full-Stack Developer opportunities.",
"email": "hello@example.test", "email": "hello@example.test",
"social": [{ "service": "GitHub", "url": "https://github.com/example" }], "social": [{ "service": "GitHub", "url": "https://github.com/example" }],
"cvMessage": "For a current CV or role-specific information, contact me directly." "cvMessage": "For a current CV or role-specific information, contact me directly."

View File

@@ -15,10 +15,23 @@ describe('approved public portfolio projection', () => {
new URL('file:///prepared-assets/'), new URL('file:///prepared-assets/'),
); );
expect(content.identity).toEqual(expect.objectContaining({ expect(content.identity).toMatchObject({
name: 'Luca Pîslaru', name: 'Luca Pîslaru',
role: 'System Administrator & Full-Stack Developer', role: 'System Administrator & Full-Stack Developer',
})); location: { city: 'Bucharest', country: 'Romania', countryCode: 'RO' },
});
expect(content.experience[0]).toMatchObject({
organization: 'Trupa lui Răzuș',
role: 'Full-Stack Developer & Hosting Administrator (Collaborator)',
});
expect(content.infrastructure.featuredOperation.outcome).toMatch(/without data loss.*roughly a year/iu);
expect(content.projects.selected.map(({ id }) => id)).toEqual([
'private-medication-tracker',
'footy-tracker',
'opinion-dynamics-simulation',
]);
expect(content.projects.selected[1]?.contribution).toMatch(/collaborator/iu);
expect(content.projects.selected[1]?.outcome).toMatch(/0\.942 mAP@0\.5.*0\.198/iu);
expect(content.systemsOperations.title).toBe('Professional systems operations'); expect(content.systemsOperations.title).toBe('Professional systems operations');
expect(content.infrastructure.title).toBe('Secure self-hosted platform and lifecycle'); expect(content.infrastructure.title).toBe('Secure self-hosted platform and lifecycle');
expect(content.projects.selected).toHaveLength(3); expect(content.projects.selected).toHaveLength(3);
@@ -59,7 +72,9 @@ describe('approved public portfolio projection', () => {
canonicalUrl: 'https://portfolio.localhost/', canonicalUrl: 'https://portfolio.localhost/',
buildId: 'preview-semantic-v1', buildId: 'preview-semantic-v1',
}); });
expect(JSON.stringify(content)).not.toMatch(/Arch Linux|CV download|heatmap|expert|percent/i); expect(JSON.stringify(content)).not.toMatch(
/Arch Linux|Mihai Razus PFA|DevOps collaborator|CV download|heatmap|fully secure|production-ready|expert|percent/iu,
);
}); });
it('publishes only the four verified owner repositories and no private project links', async () => { it('publishes only the four verified owner repositories and no private project links', async () => {

View File

@@ -37,6 +37,12 @@ const infrastructure: InfrastructureCaseStudy = {
summary: "Routine maintenance and investigation.", summary: "Routine maintenance and investigation.",
}, },
], ],
featuredOperation: {
title: "Synthetic stateful service migration",
context: "A representative migration preserved application state.",
actions: ["Prepared versions.", "Created recovery checkpoints.", "Validated the new service."],
outcome: "The synthetic migration retained expected data.",
},
}; };
const privateProject: PublicProject = { const privateProject: PublicProject = {
@@ -159,6 +165,7 @@ describe("semantic section components", () => {
it("renders a visible mailto contact and safe verified social links", () => { it("renders a visible mailto contact and safe verified social links", () => {
const contact: ContactContent = { const contact: ContactContent = {
introduction: "Open to System Administrator and Full-Stack Developer opportunities.",
email: "hello@example.test", email: "hello@example.test",
cvMessage: cvMessage:
"For a current CV or role-specific information, contact me directly.", "For a current CV or role-specific information, contact me directly.",

View File

@@ -15,11 +15,23 @@ const validContent = fixture('portfolio-content.valid.json');
const validManifest = fixture('asset-manifest.valid.json'); const validManifest = fixture('asset-manifest.valid.json');
const validSite = fixture('site-config.preview.json'); const validSite = fixture('site-config.preview.json');
type MutableContentFixture = { type MutableContentFixture = {
identity: {
location: { city: string; country: string; countryCode: string };
availability: string;
};
navigation: Array<{ id: string }>; navigation: Array<{ id: string }>;
profile: { introduction: string }; profile: { introduction: string };
contact: { social: Array<{ service: string; url: string }> }; contact: { introduction: string; social: Array<{ service: string; url: string }> };
infrastructure: { featuredOperation: { actions: string[] } };
projects: { projects: {
selected: Array<{ visibility: string; url?: string; assetIds: string[]; title: string }>; selected: Array<{
visibility: string;
url?: string;
assetIds: string[];
title: string;
media?: Array<{ assetId: string; alt: string; kind: 'image' }>;
mediaLayout?: 'single' | 'portrait-strip' | 'chart-pair';
}>;
additional: Array<{ assetIds: string[] }>; additional: Array<{ assetIds: string[] }>;
}; };
toolkit: Array<{ technologies: Array<{ assetId?: string }> }>; toolkit: Array<{ technologies: Array<{ assetId?: string }> }>;
@@ -41,6 +53,25 @@ describe('content schemas', () => {
expect(parseSiteConfig(validSite).environment).toBe('preview'); expect(parseSiteConfig(validSite).environment).toBe('preview');
}); });
it('requires public availability, city-level location, featured operations evidence, and contact copy', () => {
const content = cloneContent();
expect(content.identity.location).toEqual({ city: 'Bucharest', country: 'Romania', countryCode: 'RO' });
expect(content.identity.availability).toMatch(/Bucharest.*remote/iu);
expect(content.infrastructure.featuredOperation.actions.length).toBeGreaterThanOrEqual(3);
expect(content.contact.introduction).toMatch(/System Administrator.*Full-Stack Developer/iu);
delete (content.identity as Partial<typeof content.identity>).availability;
expect(() => parsePortfolioContent(content)).toThrow();
});
it('accepts only the approved project media layouts', () => {
const content = cloneContent();
content.projects.selected[0]!.mediaLayout = 'portrait-strip';
expect(() => parsePortfolioContent(content)).not.toThrow();
content.projects.selected[0]!.mediaLayout = 'carousel' as never;
expect(() => parsePortfolioContent(content)).toThrow(/mediaLayout|unrecognized/iu);
});
it('accepts an empty manifest before Phase 3 assets are prepared', () => { it('accepts an empty manifest before Phase 3 assets are prepared', () => {
expect(parsePublicAssetManifest({ assets: [] })).toEqual({ assets: [] }); expect(parsePublicAssetManifest({ assets: [] })).toEqual({ assets: [] });
}); });
@@ -117,6 +148,7 @@ describe('content schemas', () => {
const content = cloneContent(); const content = cloneContent();
content.projects.selected[0]!.title = 'Private self-hosted medication tracker'; content.projects.selected[0]!.title = 'Private self-hosted medication tracker';
for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = []; for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = [];
delete content.projects.selected[0]!.media;
delete content.toolkit[0]!.technologies[0]!.assetId; delete content.toolkit[0]!.technologies[0]!.assetId;
delete content.metadata.socialPreviewAssetId; delete content.metadata.socialPreviewAssetId;
expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/'))).not.toThrow(); expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/'))).not.toThrow();
@@ -126,6 +158,7 @@ describe('content schemas', () => {
const content = cloneContent(); const content = cloneContent();
content.projects.selected[0]!.title = 'Private self-hosted medication tracker'; content.projects.selected[0]!.title = 'Private self-hosted medication tracker';
for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = []; for (const project of [...content.projects.selected, ...content.projects.additional]) project.assetIds = [];
delete content.projects.selected[0]!.media;
content.toolkit[0]!.technologies[0]!.assetId = 'missing-toolkit-asset'; content.toolkit[0]!.technologies[0]!.assetId = 'missing-toolkit-asset';
expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/'))) expect(() => validateProductionInputs(content, { assets: [] }, validSite, new URL('file:///prepared/')))
.toThrow(/unmanifested asset reference: missing-toolkit-asset/i); .toThrow(/unmanifested asset reference: missing-toolkit-asset/i);
@@ -205,6 +238,7 @@ describe('content schemas', () => {
it('rejects an asset reference absent from the manifest', () => { it('rejects an asset reference absent from the manifest', () => {
const content = cloneContent(); const content = cloneContent();
content.projects.selected[0]!.assetIds = ['missing-asset']; content.projects.selected[0]!.assetIds = ['missing-asset'];
content.projects.selected[0]!.media![0]!.assetId = 'missing-asset';
expect(() => validateProductionInputs(content, validManifest, validSite, new URL('file:///prepared/'))) expect(() => validateProductionInputs(content, validManifest, validSite, new URL('file:///prepared/')))
.toThrow(/unmanifested asset/i); .toThrow(/unmanifested asset/i);
}); });
@@ -217,6 +251,7 @@ describe('content schemas', () => {
const unreferencedManifestAsset = cloneContent(); const unreferencedManifestAsset = cloneContent();
unreferencedManifestAsset.projects.selected[0]!.assetIds = []; unreferencedManifestAsset.projects.selected[0]!.assetIds = [];
delete unreferencedManifestAsset.projects.selected[0]!.media;
expect(() => validateProductionInputs(unreferencedManifestAsset, validManifest, validSite, new URL('file:///prepared/'))) expect(() => validateProductionInputs(unreferencedManifestAsset, validManifest, validSite, new URL('file:///prepared/')))
.toThrow(/unreferenced manifest asset/i); .toThrow(/unreferenced manifest asset/i);
}); });