From 16f415dd773f9da4488f3045e1ad908526bf7adc Mon Sep 17 00:00:00 2001 From: Axeloooo Date: Sun, 2 Aug 2026 16:33:01 -0400 Subject: [PATCH 1/2] feat: add Scotiabank AI Engineer role and update researcher end date Add Scotiabank work experience entry with logo, and set the UCalgary Undergraduate Researcher end date to Apr. 2026 now that it has a defined end. --- .claude/settings.json | 5 ++++ AGENTS.md | 49 ------------------------------------- package-lock.json | 14 ----------- public/scotiabank-logo.png | Bin 0 -> 20215 bytes src/data/resume.tsx | 13 +++++++++- 5 files changed, 17 insertions(+), 64 deletions(-) create mode 100644 .claude/settings.json delete mode 100644 AGENTS.md create mode 100644 public/scotiabank-logo.png diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0f243d7 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "superpowers@claude-plugins-official": true + } +} diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 891b1a3..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,49 +0,0 @@ -# AGENTS.md - -This file provides guidance to AI Agents when working with code in this repository. - -## Commands - -```bash -npm run dev # Start development server -npm run build # Build for production -npm run lint # Run ESLint -npm run start # Start production server -``` - -No test suite is configured. - -## Architecture - -This is a **Next.js 14 App Router** portfolio site built with TypeScript, Tailwind CSS, and shadcn/ui. - -### Content Data Layer - -All portfolio content lives in a single file: `src/data/resume.tsx`. This exports a `DATA` object containing personal info, work experience, education, skills, projects, and social links. **This is the primary file to edit when updating portfolio content.** - -### Page Structure - -- `src/app/page.tsx` — Home page assembling all portfolio sections (hero, work, education, projects, skills, contact) -- `src/app/blog/page.tsx` — Blog listing -- `src/app/blog/[slug]/page.tsx` — Individual blog posts rendered from MDX -- `src/app/layout.tsx` — Root layout with theme providers and analytics - -### Blog System - -Blog posts are `.mdx` files in `content/`. The `src/data/blog.ts` utility handles parsing frontmatter (via `gray-matter`) and converting markdown to HTML using a remark/rehype pipeline with `rehype-pretty-code` (Shiki) for syntax highlighting. - -### Component Layers - -- `src/components/ui/` — shadcn/ui primitives (do not edit directly; regenerate via shadcn CLI) -- `src/components/magicui/` — Custom animation components (`BlurFade`, `BlurFadeText`, `Dock`) -- `src/components/` — Feature components (`ProjectCard`, `ResumeCard`, `HackathonCard`, etc.) - -### Styling - -- Tailwind CSS with HSL CSS variables for theming (defined in `src/app/globals.css`) -- Dark mode via `class` strategy with `next-themes` -- Theme-aware favicons handled by `src/components/favicon-switcher.tsx` - -### Path Alias - -`@/*` maps to `src/*` — use this for all internal imports. diff --git a/package-lock.json b/package-lock.json index 76c6472..61ff4ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1573,7 +1573,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -1585,7 +1584,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -1641,7 +1639,6 @@ "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -2219,7 +2216,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3321,7 +3317,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -5162,7 +5157,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -6093,7 +6087,6 @@ "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", "license": "MIT", - "peer": true, "dependencies": { "@next/env": "14.2.35", "@swc/helpers": "0.5.5", @@ -6580,7 +6573,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6799,7 +6791,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6812,7 +6803,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -7282,7 +7272,6 @@ "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.29.2.tgz", "integrity": "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg==", "license": "MIT", - "peer": true, "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", @@ -7796,7 +7785,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -7945,7 +7933,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8146,7 +8133,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/public/scotiabank-logo.png b/public/scotiabank-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ad7c428e9a75d38673b5e46697d3c7d081ae26e GIT binary patch literal 20215 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 Date: Sun, 2 Aug 2026 17:43:56 -0400 Subject: [PATCH 2/2] docs: document squash-merge and conventional commit workflow Add contributing/workflow sections to README.md and CLAUDE.md explaining the devel -> main branch flow, the requirement to always squash-merge PRs, and the Conventional Commits format semantic-release relies on. --- CLAUDE.md | 16 ++++++++++++++++ README.md | 17 +++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 9cedc57..17fb39a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,3 +47,19 @@ Blog posts are `.mdx` files in `content/`. The `src/data/blog.ts` utility handle ### Path Alias `@/*` maps to `src/*` — use this for all internal imports. + +## Git Workflow & Releases + +Branch model: feature branches → `devel` → `main`. Both `devel` and `main` are protected by a repository ruleset (no direct pushes, no force-pushes, no branch deletion, PRs required). + +Versioning is fully automated by `semantic-release` (config in `release.config.js`, workflow in `.github/workflows/release.yml`), which only runs on pushes to `main` and reads commit messages via `@semantic-release/commit-analyzer` (Angular preset) to decide the version bump. + +**This means every PR into `devel` or `main` must be merged with "Squash and merge", never "Rebase and merge" or "Create a merge commit".** Rebase/merge-commit strategies leave multiple non-squashed commits (or, in the case of GitHub's rebase-and-merge, commits whose signature gets silently dropped on replay) in the history instead of one clean, conventional-commit-formatted entry — this has already caused a signature-verification incident on `devel` that had to be fixed by rewriting history. + +The PR title becomes the squash commit message and must follow [Conventional Commits](https://www.conventionalcommits.org/): +- `feat: ...` → minor bump +- `fix: ...` → patch bump +- `feat!: ...` or a `BREAKING CHANGE:` footer → major bump +- `chore:`, `docs:`, `refactor:`, etc. → no release + +`.github/workflows/ci.yml` (lint + build) runs on every PR targeting `main` or `devel`. diff --git a/README.md b/README.md index 10debb2..82c57b0 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ My personal portfolio website built with Next.js, showcasing my work experience, - [Tech Stack](#tech-stack) - [Getting Started](#getting-started) - [Commands](#commands) +- [Contributing](#contributing) - [License](#license) ## Tech Stack @@ -50,6 +51,22 @@ The development server starts at `http://localhost:3000`. | `npm run start` | Start production server | | `npm run lint` | Run ESLint | +## Contributing + +This repo uses [Conventional Commits](https://www.conventionalcommits.org/) and [semantic-release](https://semantic-release.gitbook.io/) to automate versioning and GitHub releases from `main`. + +1. Branch off `devel` (e.g. `feature/my-change`, `fix/my-bug`). +2. Open a PR into `devel`. The CI workflow (lint + build) runs automatically. +3. **Always merge with "Squash and merge"** — never "Rebase and merge" or "Create a merge commit". Every PR must land as a single commit so semantic-release can read the history correctly. +4. Give the PR a [Conventional Commits](https://www.conventionalcommits.org/) title, since it becomes the squash commit message: + - `feat: ...` → minor version bump + - `fix: ...` → patch version bump + - `feat!: ...` or a `BREAKING CHANGE:` footer → major version bump + - `chore:`, `docs:`, `refactor:`, etc. → no release +5. When `devel` is ready to ship, open a PR from `devel` into `main` and squash-merge it the same way. The push to `main` triggers the release workflow, which runs semantic-release to tag a version and publish a GitHub release from the commit history. + +`main` and `devel` are both protected branches: direct pushes, force-pushes, and branch deletion are blocked, and all changes must go through a reviewed PR. + ## License Licensed under the [Apache License, Version 2.0](LICENSE).