What this demo shows

Legacy apps, maintained and extended with AI

Using Claude Code, Anthropic's AI coding assistant, a suite of legacy applications was brought into a working local development environment, one core app was fully rewritten in a modern framework, others received targeted bug fixes and accessibility improvements — in approximately 15–20 hours of active AI-assisted work across several sessions.

⚠️

Before drawing conclusions from this demo — please read the Honest Assessment section at the bottom of this page. It addresses important limitations of this pilot and what responsible production use requires.

🏗️
A full local copy of Pharos360, its Rails API, and PostgreSQL database now runs in Docker on a single laptop — enabling development and testing without touching production or AWS.
♻️
The Present attendance system — originally a Java/GWT application — was completely rewritten as a modern Laravel/PHP app. It runs today, connected to live Pharos360 data, with all original features plus accessibility improvements.
📅
The Timefree scheduling app — which handles appointments, availability, and Google/Microsoft calendar sync — was debugged and restored to working order, including a calendar sync fix that had been silently broken.
Runs in Docker — start with docker compose up -d timefree timefree-worker from Server/.
🔍
Legacy Code Archaeology
Claude Code traced obscure bugs through decades-old PHP and Rails code: broken path resolution, missing database sequences, undefined functions, and misconfigured auth — diagnosing issues that would have taken days to find manually.
Accessibility (WCAG)
Both Pharos360 and Present received targeted accessibility fixes: keyboard navigation, screen-reader roles, focus management, contrast improvements, and proper semantic markup.
🎨
Modern Theme Toggle
Pharos360 and Present each gained a live "Classic / Modern" theme switcher — visible in the demo — that updates typography, spacing, and layout without breaking any existing functionality or client branding.
📋
Documentation
The entire local dev environment — all four projects, Docker commands, credentials, known gotchas, console shortcuts — was captured in a living reference document during the session.
💬
A standalone discussion facilitation tool — not currently part of the product suite — that was modernized as an exploratory project. The legacy stack was containerized and a full rewrite in React was begun, with a working UI and a key session bug resolved.

Live apps running right now

Open these in your browser

Pharos360
The full legacy PHP application — student profiles, cases, launchpad, email — running locally with fake student data and a placeholder logo.

Login: pharos.maintenance / Maintenance!
Open Pharos360
Present (Attendance)
The rewritten Laravel app. Browse courses, take attendance, view history, export rosters. Single-sign-on with Pharos360 — log in there first, then open Present.

Standalone login: F0012 / test
Open Present
Timefree (Scheduling)
Appointment scheduling, availability management, and Google/Microsoft calendar sync. Runs in Docker — start with docker compose up -d timefree timefree-worker from Server/. Log in at /sessions/new.

Demo flow: Open the faculty view, create an availability slot, then open the student view in an incognito window and book it.
Faculty view Request appointment (incognito)
Headsup (bonus)
Standalone facilitation app — not part of the current product suite. Both the legacy stack (Node 6 + Sencha Touch) and the in-progress React rewrite run in Docker simultaneously.

Start with docker compose up -d from Server/. Facilitator logs in at /; participants join at /join.
Facilitator view Join session (participant)

Before this goes any further

Honest assessment: what this pilot does and doesn't show

This pilot was designed to be a starting point, not a blueprint for production

The work demonstrated here — standing up local environments, rewriting an app, fixing bugs, improving accessibility — represents tasks where AI assistance is genuinely effective. It does not represent the full picture of what responsible AI-assisted development requires before code touches production systems with real student data.

🧪
No real data was used
All work was done against fake students, placeholder credentials, and a local database with no connection to production. Auth code, session handling, and data access patterns all behave differently — and carry real risk — when real student records are behind them.
🔍
No peer review or test suite
Every change went from AI output directly to running code. In a team environment that is a red flag. AI does not flag uncertainty the way a cautious developer does — it produces complete-looking code regardless of whether a case has been handled securely.
🏗️
Greenfield is easier than incremental
Rewriting Present from scratch is a better fit for AI assistance than carefully modifying a live production system where a one-line change can have downstream effects across integrations, edge cases in real data, and behavior that only appears under load.
⚖️
AI is confidently wrong
AI assistance does not understand the security implications of your specific data model, your compliance obligations, or your infrastructure. It can produce code that looks correct and works in testing but handles a case insecurely. Human review is not optional — it is the control that makes this viable.

What responsible production use requires

Developer review of every change

AI-generated code should be treated like a pull request from a junior developer — read, questioned, and approved by someone who understands the system before it runs anywhere near production.

A testing layer before deployment

The absence of a test suite was the single biggest risk in this pilot. Before AI-assisted changes reach production, automated tests need to exist so that regressions are caught before deployment, not after.

Defined scope for AI involvement

AI is most appropriate for scaffolding, diagnosis, boilerplate, and refactoring. Security-sensitive code — auth, session handling, data export, anything touching student PII — warrants extra scrutiny and should not be AI-generated without explicit review.

Staged rollout with a rollback plan

A CI/CD pipeline with a staging environment, automated testing, and a clear rollback path is the infrastructure that makes AI-assisted development safe at scale. That pipeline does not yet exist for these apps.

Technical breakdown

What was built and how

A full account of the Docker infrastructure, Pharos360 fixes, the Present rewrite, Timefree containerization, and the directory reorganization — with the key decisions and gotchas that came up along the way.

Pharos360 PHP app Present Laravel app Timefree Rails app Headsup Node app Docker Compose v2 PostgreSQL 12 MongoDB 3.4 + 4.4
Docker Local Dev Infrastructure
  • Services
    Service Description Port
    dbPostgreSQL 12 — shared by Pharos360, Present, Timefree5432
    pharos360Ubuntu 22 + PHP 8.3 + Apache + Rails 3.x API (via rbenv)80
    presentPHP 8.3 + Laravel — attendance rewrite8080
    timefreeRails 4.2 + Ruby 2.7.8 (rbenv) — scheduling web app3002
    timefree-workerDelayed Job worker — starts after timefree health check passes
    headsupNode 6 + Sencha Touch — legacy facilitation stack3000
    headsup-nextExpress 5 + Socket.io 4 — rewrite API server3001
    headsup-clientReact 19 + Vite 6 + Tailwind v4 — rewrite frontend5173
    headsup-mongoMongoDB 3.4 — legacy stack databaseinternal
    headsup-mongo-nextMongoDB 4.4 — rewrite database (also holds seed data)internal
    Start all: cd Server/ && docker compose up -d
  • Volume mounts Live bind mounts — edit code on the Mac, see changes immediately in the container. No rebuild needed for PHP/Blade edits.
  • DB persistence Named volume pgdata survives docker compose down. Only docker compose down -v wipes it.
  • Critical gotcha configuration.php is generated by the container entrypoint on every startup — it is not a static file. It writes $CLIENT = 'localdev', which all file-path resolution depends on. If this ever breaks, rebuild the image: docker compose up -d --build pharos360
  • URL requirement Must use http://localdev.pharos360.com, never http://localhost. The client() function reads the subdomain to build paths like /var/www/sos/localdev/site/…. Localhost = empty subdomain = double-slash paths = broken includes and a stuck "Loading…" header.
  • supervisord Manages Apache + Rails Thin server inside the pharos360 container. Apache proxies /api/… → Rails on port 3000, stripping the prefix.
  • docker-compose v1 Not available on this machine. Always use docker compose (v2 plugin, space not hyphen).
PHP Pharos360 — Bug Fixes & Fixes
  • cases/queries.php Multiple scalar subqueries failed with "more than one row returned." Fixed by converting = (SELECT …) to EXISTS (SELECT 1 …) in 3 places, and adding LIMIT 1 to 4 others.
  • airbrake.php The Airbrake PHP library throws a fatal exception if projectKey is an empty string. Blanked to a no-op stub — notifyAirbrake() still exists but does nothing in local dev.
  • photo.php Fake-user image check (Q-prefix student IDs) moved before the session check so avatar images serve without a valid Pharos session — needed by Present's photo proxy.
  • emails/functions.php Added missing user_to_email() function, referenced everywhere but never defined. Looks up email from students table (students) or names table (staff) by username.
  • DB sequences All PostgreSQL ID sequences were out of sync after bulk import. Reset every _id_seq to MAX(id) of its table.
  • delayed_jobs table Table was entirely missing. Created with standard schema plus the custom client VARCHAR(255) column that Pharos::Delayed::Job requires.
  • Rails client fallback PHP calls the Rails API as http://127.0.0.1/api/… (no subdomain). Added fallback in application_controller.rb: when host matches 127.0.0.1, sets client = 'localdev'.
  • Student avatars Generated 40 placeholder PNG avatars for fake students (Q1001–Q1039) using PHP GD. Script at Server/generate_avatars.php.
Laravel 11 Present — Full Rewrite (Java/GWT → PHP)
  • What it replaced A Java/GWT application. The rewrite is a Laravel 11 PHP app that shares the existing Pharos360 PostgreSQL database (roster_* tables) — no data migration needed.
  • Auth Reads pharos360_session_id cookie first, falls back to PHPSESSID (set by Pharos360 on every page load). Both validated via internal HTTP call to http://pharos360/partials/cookie_checker.php. Both cookies excluded from Laravel encryption in bootstrap/app.php.
  • Duplicate rows class_faculty and view_class_list_filtered_with_names contain duplicates. All queries use DISTINCT.
  • phpspreadsheet v5 ByColumnAndRow() methods removed in v5. Replaced with Coordinate::stringFromColumnIndex($col) . $row.
  • Spark lines Per-student attendance history rendered as colored vertical bars (4px wide). Built in CourseController::buildSparkLines(), reused by AttendanceController.
  • Learn Names Students shuffled on each visit. studentsJson prepared in the controller (not in Blade) to avoid PHP parser issues with fn() closures inside @json().
  • Features Course list sidebar, Take Attendance (photo cards, cycle status, bulk email), History, Summary, Export (CSV + XLSX), Learn Names modal, Event type management, per-user Settings.
WCAG Accessibility Improvements (both apps)
  • Pharos360 Skip-to-main link, role="main", role="navigation", role="banner", keyboard-accessible app menu (role="button", tabindex, aria-expanded), flash message roles (role="alert" / "status"), viewport meta tag added globally.
  • Present Student cards: role="button", tabindex="0", Enter/Space keyboard handlers, dynamic aria-label as status cycles. Learn Names modal: focus trap, Escape closes, focus returns to trigger. Spark lines: role="img" + aria-label. Contrast: #aaa#6b7280.
  • Modern theme Both apps got a floating Classic/Modern toggle (bottom-right, persisted via localStorage). All modern styles scoped to html[data-theme="modern"]. Anti-FOUC inline script applies saved theme before first paint. Client color system left intact in Pharos360.
Node / React Headsup — Facilitation App (bonus — not current product suite)
  • What it does Facilitates structured group discussions: a facilitator runs sessions through a state machine (init → started → grouping → discussing → reporting → evaluating → closed), participants join on mobile, and the facilitator drives prompts and evaluations in real time via Socket.io.
  • Legacy stack Node.js + Express 3.2.4, MongoDB (mongoskin), Socket.io 0.9.14, Sencha Touch (Ext JS mobile) compiled client, Google OAuth — port 3000. Runs in Docker with platform: linux/amd64 required for Apple Silicon.
  • Rewrite stack Node.js + Express 5, Socket.io 4, MongoDB driver v6, React 19 + Vite 6 + Tailwind v4, Google OAuth. Server on port 3001, client dev server on port 5173. Both run simultaneously in Docker alongside the legacy stack.
  • Key bug fixed Stale device cookie: middleware/device.js trusted the browser's device cookie without verifying the document existed in MongoDB. After a volume wipe, the cookie pointed to a deleted device, causing group.js to find 0 active sockets and throw "No participants have joined." Fix: middleware now verifies the device in DB before trusting the cookie; falls back to creating a new one. Socket updateOne also uses upsert: true.
  • Styling pass Full modern UI pass on the React rewrite: Inter font, indigo-600 primary, slate neutrals, card shadows, AdminPanel as a numbered step flow (1–4 with checkmarks), WaitingRoom with prominent join code and pulsing participant dot, mobile-first participant views, SVG icons replacing decorative emojis.
  • Auth Google OAuth with hardcoded credentials in lib/authentication.js — still active. Session stored in MongoDB (web.sessions collection).
  • Start command Run docker compose up -d from Server/ — Headsup is now merged into the main compose. To rebuild only the rewrite: docker compose up headsup-next headsup-client --build -d from Server/.
  • Seed defaults Roles and prompt sets (Six Thinking Hats, SWOT Analysis, Pro/Con, Generic Prompts) are not auto-seeded on first start. Log in via Google OAuth first — the seed looks up the first user in the DB and will error if none exists. Then run once from the Claude/ root:
    docker compose -f Server/docker-compose.yml exec -T headsup-mongo-next mongo < Headsup/seed.js
    Safe to re-run — clears and re-inserts the defaults each time.
Rails 4.2 Timefree — Scheduling & Calendar Sync
  • What it does Appointment scheduling, staff availability management, and bidirectional sync with Google Calendar and Microsoft (via Graph API). Separate from the Pharos360 Docker network — has its own database.
  • Stack Rails 4.2 (via RailsLTS subscription — security patches backported to 4.2), Ruby 2.7.8, PostgreSQL. Runs in Docker alongside Pharos360 and Present — shares the same db container network. Available at http://localhost:3002.
  • Starting the app cd Server/ && docker compose up -d timefree timefree-worker. Login page: http://localhost:3002/sessions/new. The docker compose up command also works to start all services at once.
  • Delayed Job worker The timefree-worker container starts automatically once the web container passes its health check. It processes Google Calendar sync and other background jobs. Check status with docker compose ps — should show running.
  • Ruby / image Ruby 2.7.8 is installed via rbenv inside the Docker image (Server/Dockerfile.timefree). No local Ruby or mise required. First build takes ~15 min (compiles Ruby from source); subsequent starts are fast via layer cache.
  • Standalone mode BYPASS_PHAROS_360=true in config/env_var.rb — email-only login, no password, no Pharos360 session check. Full integration requires both apps on .pharos360.com subdomains via /etc/hosts.
  • Google Calendar sync fix Timefree was sending Google API webhook notifications to an invalid port by default. Fixed by correcting the callback URL and disabling SSL verification on that internal route (3f2730a).
  • Availability blocking fixes Two edge-case bugs fixed: events overlapping the start of an availability window, and events overlapping the end — both were failing to block the slot correctly (d1fa282, 38e7142).
  • Known issue All-day Google Calendar events silently block availabilities — the week view doesn't show all-day events, so the user gets no indication of why their availability isn't appearing. Needs a warning or fix.
  • Other fixes Google Calendar delete support, inaccessible calendar checks, external calendar sorting, option to have no default sync calendar, removed dead-end "Update" button from user settings, excessive sync log suppression.
Git Repository & Directory Cleanup
  • Before A Projects/ subfolder contained Present and Beacon docs alongside the top-level repo clones, causing confusion about which copy was authoritative.
  • After Present/ moved to root of Claude/. Projects/ removed. Docker volume mount updated from ../Projects/Present../Present. Image rebuilt.
  • .gitignore Added GoogleCreds (irreplaceable Google API credentials, no longer re-exportable from Google Console) and .DS_Store.
  • Separate git repos Pharos360-2.x/ has its own .git (branch master). Present/ has its own .git. The root Claude/ repo tracks infrastructure files and this launchpad.
  • Timefree Separate Rails scheduling app — now Dockerized alongside Pharos360 and Present. Two containers: timefree (web, port 3002) and timefree-worker (Delayed Job). Start with docker compose up -d from Server/.