3 Commits

Author SHA1 Message Date
4331b0659f add automated review
All checks were successful
Build and Release Resume PDF / date-fetch (push) Successful in 16s
Check flake.lock / Check health of `flake.lock` (push) Successful in 12s
Check Nix flake / Perform Nix flake checks (push) Successful in 59s
Build and Release Resume PDF / build (push) Successful in 1m27s
2026-03-27 16:49:38 -04:00
3c72938f98 move pdfs to lfs
All checks were successful
Check flake.lock / Check health of `flake.lock` (push) Successful in 18s
Check Nix flake / Perform Nix flake checks (push) Successful in 1m39s
2026-03-27 16:18:41 -04:00
311f022b66 ignore pdfs 2026-03-27 16:17:36 -04:00
13 changed files with 214 additions and 23 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
*.pdf filter=lfs diff=lfs merge=lfs -text

View File

@@ -29,7 +29,7 @@ If the build produces errors, include them as a finding under **Formatting & Lay
Clean up any stale preview files, then convert the first page of the built PDF to a PNG and view it:
```
rm -f resume-preview*.png
rm -f resume-preview*.png || true # ignore if no previews exist
pdftoppm -r 150 -png resume.pdf resume-preview
```

View File

@@ -0,0 +1,189 @@
name: "Daily Resume Review"
on:
workflow_dispatch:
schedule:
- cron: "15 7 * * *"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
review:
name: "Run AI resume review and open PR"
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install nix
uses: https://github.com/DeterminateSystems/nix-installer-action@main
- name: Build resume PDF
id: build
run: |
set -euo pipefail
if nix build .#default > build.log 2>&1; then
echo "build_status=success" >> "$GITHUB_OUTPUT"
else
echo "build_status=failed" >> "$GITHUB_OUTPUT"
fi
- name: Prepare review context
id: context
run: |
set -euo pipefail
mkdir -p archive/reviews
review_date="$(date +'%Y-%m-%d')"
review_path="archive/reviews/${review_date}-automated-review.md"
python - <<'PY'
import json
import os
from pathlib import Path
prompt = Path('.github/prompts/resume-review.prompt.md').read_text(encoding='utf-8')
resume = Path('resume.tex').read_text(encoding='utf-8')
build_log = Path('build.log').read_text(encoding='utf-8', errors='replace') if Path('build.log').exists() else ''
payload = {
"model": os.getenv("OPENAI_MODEL", "gpt-5"),
"input": [
{
"role": "system",
"content": [
{
"type": "input_text",
"text": "You are an autonomous resume review agent. Follow the provided prompt exactly."
}
]
},
{
"role": "user",
"content": [
{
"type": "input_text",
"text": (
"Use the following prompt as your full instruction set:\n\n"
f"{prompt}\n\n"
"Build status from CI:\n"
f"- nix build result: {os.getenv('BUILD_STATUS', 'unknown')}\n\n"
"Build log:\n"
"```text\n"
f"{build_log[-15000:]}\n"
"```\n\n"
"Resume source:\n"
"```tex\n"
f"{resume}\n"
"```\n"
)
}
]
}
]
}
Path('review-request.json').write_text(json.dumps(payload), encoding='utf-8')
PY
echo "review_date=${review_date}" >> "$GITHUB_OUTPUT"
echo "review_path=${review_path}" >> "$GITHUB_OUTPUT"
env:
BUILD_STATUS: ${{ steps.build.outputs.build_status }}
- name: Run review agent
id: review
run: |
set -euo pipefail
api_base="${OPENAI_BASE_URL:-https://api.openai.com/v1}"
curl -sS "${api_base}/responses" \
-H "Authorization: Bearer ${OPENAI_API_KEY}" \
-H "Content-Type: application/json" \
-d @review-request.json \
> response.json
python - <<'PY'
import json
from pathlib import Path
response = json.loads(Path('response.json').read_text(encoding='utf-8'))
text = response.get('output_text', '').strip()
if not text:
chunks = []
for item in response.get('output', []):
for content in item.get('content', []):
if content.get('type') == 'output_text':
chunks.append(content.get('text', ''))
text = '\n'.join(chunks).strip()
if not text:
raise SystemExit('Agent response did not contain output text.')
review_path = Path('${{ steps.context.outputs.review_path }}')
review_path.write_text(text + '\n', encoding='utf-8')
PY
- name: Check whether review changed
id: review_guard
run: |
set -euo pipefail
current_review="${{ steps.context.outputs.review_path }}"
previous_review="$(ls -1 archive/reviews/*-automated-review.md 2>/dev/null | grep -vx "$current_review" | sort | tail -n1 || true)"
if [ -z "$previous_review" ]; then
echo "create_pr=true" >> "$GITHUB_OUTPUT"
echo "reason=no_previous_review" >> "$GITHUB_OUTPUT"
exit 0
fi
if cmp -s "$current_review" "$previous_review"; then
rm -f "$current_review"
echo "create_pr=false" >> "$GITHUB_OUTPUT"
echo "reason=review_unchanged" >> "$GITHUB_OUTPUT"
else
echo "create_pr=true" >> "$GITHUB_OUTPUT"
echo "reason=review_changed" >> "$GITHUB_OUTPUT"
fi
- name: Print guard decision
run: |
echo "create_pr=${{ steps.review_guard.outputs.create_pr }}"
echo "reason=${{ steps.review_guard.outputs.reason }}"
- name: Write PR body
if: steps.review_guard.outputs.create_pr == 'true'
run: |
cat > pr_body.md <<'EOF'
This PR contains the latest automated daily resume review.
Review file:
- ${{ steps.context.outputs.review_path }}
Generated by `.github/workflows/daily-resume-review.yml` using:
- Prompt: `.github/prompts/resume-review.prompt.md`
- Source: `resume.tex`
EOF
- name: Create Pull Request
id: create-pull-request
if: steps.review_guard.outputs.create_pr == 'true'
uses: https://nayeonie.com/ahuston-0/create-pull-request@main
with:
token: ${{ secrets.GH_TOKEN_FOR_UPDATES }}
add-paths: ${{ steps.context.outputs.review_path }}
body-path: pr_body.md
author: '"github-actions[bot]" <github-actions[bot]@users.noreply.github.com>'
title: 'automated: Daily resume review (${{ steps.context.outputs.review_date }})'
commit-message: 'automated: Daily resume review (${{ steps.context.outputs.review_date }})'
branch: automated/daily-resume-review
delete-branch: true
pr-labels: |
automated
review
permissions:
pull-requests: write
contents: write

3
.gitignore vendored
View File

@@ -12,7 +12,8 @@
*.fls
*.synctex.gz
*.png
*.pdf # all PDFs are generated, so ignore them all
# all PDFs are generated, so ignore them all
*.pdf
result
.direnv

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -249,26 +249,26 @@ to improve reliability in navigation}
% Certifications moved to Technical Skills & Certifications section above
%\section{Projects}
%\resumeSubHeadingListStart
%\resumeProjectHeading{SwitchForward}{Jun. 2020 -- Aug. 2020}
%\resumeItemListStart
%\resumeItem{A \textbf{Python}-based Telegram bot to send stock
% updates for the Nintendo Switch during a supply shortage}
%\resumeItem{Used the Gmail API to receive and parse emails from a
% Google Group tracking Nintendo Switch stock}
%\resumeItem{Sent updates to a Telegram announcements channel used by
% \textbf{5-10} users}
%\resumeItemListEnd
%
%\resumeProjectHeading{Autonomous Robot}{Aug. 2018 -- Dec. 2018}
%\resumeItemListStart
%\resumeItem{An \textbf{Arduino}-based robot designed to navigate
% through a maze}
%\resumeItem{Primarily worked on pathplanning and control in a dynamic setting}
%\resumeItem{Implemented basic error-correction to account for drift
% during navigation}
%\resumeItemListEnd
\section{Projects}
\resumeSubHeadingListStart
\resumeProjectHeading{SwitchForward}{Jun. 2020 -- Aug. 2020}
\resumeItemListStart
\resumeItem{A \textbf{Python}-based Telegram bot to send stock
updates for the Nintendo Switch during a supply shortage}
\resumeItem{Used the Gmail API to receive and parse emails from a
Google Group tracking Nintendo Switch stock}
\resumeItem{Sent updates to a Telegram announcements channel used by
\textbf{5-10} users}
\resumeItemListEnd
\resumeProjectHeading{Autonomous Robot}{Aug. 2018 -- Dec. 2018}
\resumeItemListStart
\resumeItem{An \textbf{Arduino}-based robot designed to navigate
through a maze}
\resumeItem{Primarily worked on pathplanning and control in a dynamic setting}
\resumeItem{Implemented basic error-correction to account for drift
during navigation}
\resumeItemListEnd
% Removing this project as it is not as relevant to the software
% engineering positions I am applying for
@@ -285,6 +285,6 @@ to improve reliability in navigation}
% resulting data}
%\resumeItemListEnd
%\resumeSubHeadingListEnd
\resumeSubHeadingListEnd
\end{document}