Phase 3 — Development Tools

Development Tools

Master the terminal, Git version control, and GitHub collaboration—the infrastructure every developer and AI agent depends on.

Chapters 10–12Phase Gate + TaskForge
Before You Begin Phase 3

This phase assumes you can: write functions with parameters and return values (Ch 05), use lists and dictionaries (Ch 06), and handle errors with try/except (Ch 09). You'll also need a working Python environment from Ch 03.

Chapter 10 The Terminal and Shell Scripting

Why This Matters Now

Claude Code runs entirely in the terminal. Every command it executes, every file it reads, every test it runs—all happen through the same shell commands you'll learn here. Terminal fluency isn't optional for AI-assisted development; it's the interface.

You used the terminal in Chapter 03 to run Python. Now you'll learn it as a power tool. The ~15 commands below are the ones professionals use daily. Everything else you can look up.

Essential Commands

Essential terminal commands for daily use
CommandWhat It DoesExample
cdChange directorycd ~/projects
lsList filesls -la
pwdPrint current directorypwd
mkdirCreate directorymkdir src
rmRemove filerm old.txt
cpCopycp a.py b.py
mvMove/renamemv old.py new.py
catPrint file contentscat README.md
grepSearch text in filesgrep "def " *.py
headShow first N lineshead -20 log.txt
tailShow last N linestail -f server.log
findFind filesfind . -name "*.py"

Shell Scripts

A shell script is a file of commands that run in sequence. Start every script with a shebang line that tells the OS which interpreter to use:

#!/bin/bash
echo "Running tests..."
python3 -m pytest
echo "Done."

Save as run_tests.sh, make executable with chmod +x run_tests.sh, then run with ./run_tests.sh.

Piping: Composing Small Tools

The | operator sends the output of one command to the input of another. > writes output to a file.

cat access.log 10,000 lines grep "POST" 2,400 lines grep "500" 47 lines wc -l 47
Pipes compose small tools into powerful workflows. Each stage filters or transforms the data for the next.

Environment Variables and PATH

Environment variables are system-wide settings. PATH tells the shell where to find commands. When you get command not found, it means the program isn't in any directory listed in PATH.

export API_KEY="sk-abc123"   # set a variable
echo $PATH                    # see your PATH

SSH Basics

SSH (Secure Shell) lets you connect to remote machines via the terminal. You'll encounter it when deploying code or accessing cloud servers. Generate a key pair with ssh-keygen -t ed25519, then add the public key (~/.ssh/id_ed25519.pub) to remote services (you'll set this up with a specific service in Chapter 12). Connect: ssh user@hostname.

tmux: Terminal Multiplexer

Concept: A terminal multiplexer lets you run multiple persistent shell sessions inside a single terminal window. You can split panes, switch between tabs, and detach entirely—leaving everything running in the background. This is essential for any workflow where you need to monitor multiple processes simultaneously, and it becomes critical when orchestrating multiple AI agents.

Current Tool (March 2026)

tmux (all platforms, free). Install: brew install tmux (macOS) or sudo apt install tmux (Linux). The standard terminal multiplexer for multi-agent workflows in Phase 5.

Why tmux Matters for AI Development

In Phase 5, you'll run multiple Claude Code agents simultaneously. Each needs its own terminal session. Without tmux, you'd need multiple terminal windows, and closing your laptop kills them all. With tmux, sessions persist in the background—you can disconnect, reconnect, and your agents keep running. tmux is the infrastructure that makes background agents possible.

Installing tmux

Terminal# macOS (via Homebrew) brew install tmux # Ubuntu/Debian sudo apt install tmux # Verify tmux -V

Core Concepts

tmux has three layers:

tmux organizational layers: sessions, windows, and panes
ConceptWhat It IsAnalogy
SessionA named workspace that persists in the backgroundA project desk that stays set up even when you leave the room
WindowA full-screen tab within a sessionDifferent pages on the same desk
PaneA split within a windowDividing one page into side-by-side columns

Getting Started

Every tmux command inside a session starts with the prefix key: Ctrl + B. You press the prefix, release it, then press the next key. This is the most common stumbling block—you're pressing a two-step combo, not holding everything at once.

Terminal# Start a new named session tmux new -s taskforge # You're now INSIDE tmux. The green bar at the bottom confirms it. # Detach (leave session running in background) # Press: Ctrl+B, then d # List running sessions tmux ls # Reattach to your session tmux attach -t taskforge # Kill a session when done tmux kill-session -t taskforge

tmux Hotkeys Reference

All hotkeys below assume you press Ctrl + B first (the prefix), release it, then press the next key.

Session Management
tmux hotkeys for session management
Prefix +ActionWhen to Use
dDetach from sessionLeave the session running in the background. Your agents keep working.
sList and switch sessionsInteractive session picker. Arrow keys to navigate, Enter to select.
$Rename current sessionGive meaningful names: taskforge, api-server, agent-team.
( / )Previous / next sessionCycle between sessions quickly.
Window Management (Tabs)
tmux hotkeys for window management
Prefix +ActionWhen to Use
cCreate new windowOpen a fresh full-screen terminal within the session.
nNext windowCycle forward through windows.
pPrevious windowCycle backward through windows.
0-9Jump to window by numberDirect jump: prefix + 2 goes to window 2.
,Rename current windowName windows by purpose: code, tests, logs.
wWindow list (interactive)Visual picker showing all windows across all sessions.
&Close current windowConfirm with y. Closes all panes in the window.
Pane Management (Splits)
tmux hotkeys for pane management
Prefix +ActionWhen to Use
%Split vertically (left/right)Code on the left, test output on the right.
"Split horizontally (top/bottom)Command on top, log stream on the bottom.
Arrow keysMove to adjacent paneNavigate between splits using direction.
xClose current paneConfirm with y. Closes just this pane.
zToggle pane zoom (fullscreen)Temporarily maximize one pane to see more detail. Press again to restore.
oCycle through panesQuick hop to the next pane without arrow keys.
{ / }Swap pane left / rightRearrange pane positions without recreating them.
SpaceCycle through layoutsToggle between even-horizontal, even-vertical, main-horizontal, etc.
Ctrl + ArrowResize pane by 1 cellFine-tune pane sizes. Hold for continuous resize.
Copy Mode and Scrollback
tmux hotkeys for copy mode and scrollback
Prefix +ActionWhen to Use
[Enter copy modeScroll through output history. Use arrow keys or Page Up/Down. Press q to exit.
]Paste from tmux bufferPaste text you copied in copy mode.

In copy mode, navigate with arrow keys. Press Space to start selection, move to select text, press Enter to copy. Press q to exit copy mode.

Recommended tmux Configuration

Create ~/.tmux.conf with these settings to make tmux more usable:

# ~/.tmux.conf

# Use Ctrl+A as prefix instead of Ctrl+B (easier to reach)
unbind C-b
set -g prefix C-a
bind C-a send-prefix

# Split panes with | and - (easier to remember)
bind | split-window -h -c "#{pane_current_path}"
bind - split-window -v -c "#{pane_current_path}"

# New windows open in current directory
bind c new-window -c "#{pane_current_path}"

# Navigate panes with Alt+Arrow (no prefix needed)
bind -n M-Left select-pane -L
bind -n M-Right select-pane -R
bind -n M-Up select-pane -U
bind -n M-Down select-pane -D

# Enable mouse support (click to select pane, scroll to scroll)
set -g mouse on

# Start window numbering at 1 (not 0)
set -g base-index 1
setw -g pane-base-index 1

# Renumber windows when one is closed
set -g renumber-windows on

# Increase scrollback buffer
set -g history-limit 50000

# Reduce escape delay (faster for vim/nano users)
set -sg escape-time 10

# Status bar styling
set -g status-style bg=black,fg=white
set -g status-left "[#S] "
set -g status-right "%H:%M"

After saving, reload with tmux source-file ~/.tmux.conf (or start a new session).

If You Used the Config Above

The config changes the prefix from Ctrl + B to Ctrl + A and splits with | and - instead of % and ". All the hotkey tables above use the default bindings. If you use this config, substitute Ctrl + A for Ctrl + B everywhere, and use |/- for splits.

Real-World tmux Workflow

Here's how a typical multi-agent development session looks:

Terminal# Create a session for your project tmux new -s taskforge # Window 1 (default): Claude Code main session claude # Create Window 2 for tests (prefix + c) # Name it "tests" (prefix + ,) python3 -m pytest --watch # Create Window 3 for server (prefix + c) # Name it "server" (prefix + ,) python3 -m flask run # Split Window 1 to watch git log (prefix + %) # In the right pane: watch -n 2 git log --oneline -10 # Detach and go to lunch (prefix + d) # Everything keeps running. # Come back, reattach: tmux attach -t taskforge
tmux Practice Drill

Goal: Create a session with 3 panes. Steps: (1) tmux new -s practice (2) Prefix + % to split vertically (3) Prefix + " to split the right pane horizontally (4) Prefix + arrow keys to navigate between all 3 panes (5) Prefix + z to zoom one pane, then z again to unzoom (6) Prefix + d to detach (7) tmux attach -t practice to reattach. Verification: All 3 panes are still there with their content after reattaching.

Terminal Speed: Advanced Hotkeys

You learned basic shell hotkeys in Chapter 03. Here are the advanced ones that make a real difference when you're working all day in the terminal:

Advanced terminal hotkeys for editing and navigation
HotkeyActionExample
Ctrl + RReverse search historyStart typing any part of a past command. Press Ctrl + R again to cycle through matches. Press Enter to execute, Ctrl + G to cancel.
Ctrl + X, Ctrl + EOpen current command in $EDITORFor long, complex commands: edit in a full editor, save, and it executes. Set export EDITOR=nano if not set.
Alt + .Insert last argument of previous commandJust ran mkdir -p src/components/auth? Type cd then Alt + . to get cd src/components/auth.
Ctrl + TSwap two characters before cursorFix typos like slls without retyping.
Alt + TSwap two words before cursorFix word-order mistakes.
Ctrl + _Undo last editUndo a Ctrl + W or Ctrl + U that deleted too much.
fcOpen last command in editorFix and re-run a complex command. Saves/closes to execute.

History Power Moves

Shell history shortcuts and expansion commands
CommandActionExample
!!Repeat entire last commandsudo !! — re-runs previous command with sudo.
!$Last argument of previous commandcat file.py then vim !$ opens the same file in vim.
!^First argument of previous commandLess common, but available.
!:2Second argument of previous commandAccess specific positional arguments.
^old^newReplace and re-runRan git diff mian? Type ^mian^main to fix the typo and re-execute.
history | grep pytestSearch history for a patternFind all past pytest commands.

TaskForge Connection

You'll use pipes and grep to inspect TaskForge: grep "def " taskforge.py lists all function definitions. When you manage agents in Phase 5, you'll use tmux to run multiple Claude Code sessions simultaneously—one session per agent, each in its own tmux window, all within a single session you can detach from and reattach to.

Micro-Exercises

1: Pipe Words
echo 'hello world' | wc -w

The pipe sends echo's output to the word-counter. Result: 2.

2: List First 5 Files
ls | head -5
Try This Now

In one line, find all Python files that contain the word import and count them:

Terminalgrep -rl 'import' . --include='*.py' | wc -l

Verification: A number prints (the count of matching files).

If this doesn't work: (1) grep: No such file or directory → you're in an empty directory. cd to a project first. (2) Count is 0 → no Python files in the current tree.

You just composed multiple commands into a pipeline—the same technique you'll use to inspect logs, filter test output, and monitor AI agent activity.

Interactive Exercises

Knowledge Check

Which command shows only the first 10 lines of a file?

Knowledge Check

What does the pipe operator | do in cat file.txt | grep error?

Knowledge Check

Which flag makes grep search case-insensitively?

Terminal Proficiency

File Count

Run ls -la in your TaskForge directory and paste the output below.

Chapter 11 Version Control with Git

Why This Matters Now

Git is not optional. Claude Code creates branches, makes commits, and reads your project history to understand your codebase. Without Git, you can't use Claude Code effectively—and without understanding Git, you can't supervise what Claude Code does with your code.

What Happens Without Version Control

You spend 3 hours refactoring a file. You break something. You can't remember what the working version looked like. You try to undo manually and break it worse. With Git, one command (git checkout -- file.py) restores the last working version. Without it, you're gambling with every edit.

Current Tool (March 2026)

Git is the universal version control system. Virtually all professional projects use it. Install: brew install git (macOS), apt install git (Linux), or git-scm.com (Windows).

Git tracks every change to your code. Think of it as a time machine for your project—you can go back to any previous state. Branches are like parallel timelines: you can work on a new feature in one timeline while keeping the main timeline stable. If the experiment fails, you discard that timeline. If it succeeds, you merge it back. This is how AI agents work on your code without breaking what already works.

Git Is the #1 Prerequisite for Claude Code

Claude Code reads your git history, makes commits, creates branches, and resolves conflicts. If you don't understand what it's doing with git, you can't supervise it.

Core Commands

Core Git commands for version control
CommandWhat It Does
git initCreate a new repo
git add .Stage all changes
git commit -m "msg"Save a snapshot
git statusSee what's changed
git log --onelineView commit history
git diffSee unstaged changes
git branch nameCreate a branch
git switch nameSwitch to a branch
git merge nameMerge a branch into current
c1 c2 c3 m f1 f2 main feature
Branches let you work on features without affecting main. Merging brings the work back. This is how AI agents work in parallel.

Merge Conflicts

When two branches change the same line, git can't decide which version to keep. It marks the conflict with <<<<<<</=======/>>>>>>> markers. You choose the correct version, delete the markers, and commit.

Worktrees

git worktree add ../feature-branch feature creates a separate working directory for a branch. Claude Code multi-agent setups use worktrees so each agent has its own filesystem.

TaskForge Connection

In the Phase 3 Gate, you'll turn TaskForge into a git repo with 3+ commits and a merged branch. This is how every professional project is managed.

Micro-Exercises

1: Your First Repo
git init
echo "# My Project" > README.md
git add README.md
git commit -m "initial commit"
git log --oneline
2: Branch and Switch
git branch feature
git switch feature
echo "new feature" > feature.txt
git add feature.txt
git commit -m "add feature"
git switch main

The file feature.txt disappears—it's on the other branch.

Try This Now

Init a repo, commit a file, create branch test, modify line 1 on test, switch to main, modify the same line 1 differently, git merge test. You'll get a conflict. Open the file, resolve it, commit.

Verification: git log --oneline shows at least 4 commits. The conflict markers are gone.

If this doesn't work: (1) fatal: not a git repository → you forgot git init. (2) No conflict → you modified different lines, not the same line. (3) CONFLICT message but file looks normal → open the file in an editor to see the markers.

You just created a branch, made changes, caused a merge conflict, and resolved it. This is exactly what happens when multiple AI agents work on the same codebase—and now you know how to handle it.

Interactive Exercises

Knowledge Check

What is the Git staging area (index)?

Knowledge Check

What does git diff --staged show?

Git Fundamentals

Git Log

Run git log --oneline in your TaskForge repo and paste the output.

Practice: Interactive Git

Complete the "Main" section of Learn Git Branching. It's an interactive visual tutorial that makes branching and merging click in a way that reading alone cannot.

Chapter 12 Git Remote and GitHub

Why This Matters Now

Every professional project lives on a remote repository. Claude Code workflows in later phases involve pull requests, remote branches, and CI pipelines—all of which require understanding remote Git.

What Is a Remote?

So far, all your Git work has been local—commits, branches, and merges living only on your machine. A remote is a copy of your repository hosted on another server, typically GitHub. Your local repo is where you work. The remote repo is where you share, back up, and collaborate. Think of it this way: your local repo is your notebook; the remote is the shared whiteboard everyone on the team can see.

When you push, you copy your commits to the remote. When you pull, you copy the remote's commits to your local repo. The two stay in sync through these explicit actions—nothing happens automatically.

GitHub Account Setup

If you don't have a GitHub account yet:

  1. Go to github.com and click Sign up.
  2. Choose a username, enter your email, and set a password.
  3. Once logged in, click the + icon (top right) → New repository.
  4. Name it taskforge, leave it public, and do not initialize with a README (you already have one locally).
  5. GitHub shows you the commands to connect your local repo. You'll use those next.

Connecting Local to Remote

After creating an empty repository on GitHub, connect your local repo to it:

git remote add origin https://github.com/YOUR-USERNAME/taskforge.git

origin is the conventional name for your primary remote. You can name it anything, but origin is universal. Verify the connection:

git remote -v

You should see two lines—one for fetch and one for push—both pointing to your GitHub URL.

Push and Pull

Push sends your local commits to the remote. Pull fetches the remote's commits and merges them into your local branch.

# Send your commits to GitHub
git push origin main

# Get new commits from GitHub
git pull origin main

The first time you push a brand-new branch, use -u to set up tracking:

git push -u origin main

After that, git push and git pull work without specifying the remote and branch every time.

The "Rejected Because Remote Contains Work" Error

If you see ! [rejected] main -> main (fetch first), it means the remote has commits you don't have locally. Fix it:

git pull origin main    # fetch and merge remote changes
git push origin main    # now push succeeds

If the pull creates a merge conflict, resolve it the same way you learned in Chapter 11: edit the file, remove the conflict markers, git add, and git commit.

Cloning

git clone downloads an entire repository—all files, all history, all branches—from a remote to your machine:

git clone https://github.com/someone/their-project.git

This creates a new directory named their-project/ with a fully functional Git repo inside. The remote origin is already set up, pointing back to the URL you cloned from. This is how you get someone else's project onto your machine.

Branches on Remotes

Branches exist independently on your local machine and on the remote. To push a local branch to GitHub:

git push origin feature-branch

This creates the branch on GitHub if it doesn't exist. To set up tracking (so Git knows the local branch corresponds to the remote one):

git push -u origin feature-branch

After tracking is set, git push and git pull automatically know which remote branch to use. To see all branches, including remote ones:

git branch -a

.gitignore

A .gitignore file tells Git which files and directories to never track. This keeps your repository clean by excluding generated files, secrets, and environment-specific artifacts.

# Byte-compiled Python files
*.pyc
__pycache__/

# Virtual environments
.venv/
venv/

# Environment variables / secrets
.env

# IDE settings
.vscode/
.idea/

# OS files
.DS_Store

# Node (if applicable)
node_modules/

Pattern syntax:

.gitignore pattern syntax and matching rules
PatternWhat It Matches
*.pycAll files ending in .pyc, in any directory
__pycache__/Any directory named __pycache__ and everything inside it
.envA file named .env in the project root
.venv/The .venv directory and all its contents
node_modules/The node_modules directory (JavaScript dependencies)
Why .gitignore Matters

Without .gitignore, you risk committing secrets (.env files with API keys), bloating your repo with hundreds of megabytes of dependencies (node_modules/, .venv/), and pushing OS junk (.DS_Store) that clutters every diff. Add .gitignore before your first commit.

Pull Requests

A pull request (PR) is a proposal to merge one branch into another. It's the core of code review: you show your changes, teammates review them, and only after approval does the code merge into main. The workflow:

  1. Branch: Create a feature branch locally. git switch -c add-delete-feature
  2. Work: Make changes, commit them.
  3. Push: git push -u origin add-delete-feature
  4. Open PR: Go to GitHub. You'll see a banner: "Compare & pull request." Click it. Write a description of what you changed and why.
  5. Review: Teammates read your code, leave comments, request changes.
  6. Merge: Once approved, click "Merge pull request" on GitHub.

Even solo developers use PRs—they create a clean record of what changed and why, and they're how Claude Code submits work for your review in later phases.

SSH Keys

GitHub supports two connection methods: HTTPS and SSH. HTTPS asks for credentials on every push. SSH uses a key pair so you authenticate automatically.

Generate an SSH key:

ssh-keygen -t ed25519 -C "your-email@example.com"

Press Enter to accept the default location. Set a passphrase or leave it empty. Then add the public key to GitHub:

  1. Copy your public key: cat ~/.ssh/id_ed25519.pub
  2. GitHub → Settings → SSH and GPG keys → New SSH key
  3. Paste the key and save.

Test the connection:

ssh -T git@github.com

You should see: Hi username! You've successfully authenticated. Now you can use SSH URLs (git@github.com:user/repo.git) instead of HTTPS.

Local Repo your machine GitHub remote server push pull New Local Copy another machine clone
Push sends commits from local to GitHub. Pull brings commits from GitHub to local. Clone creates a brand-new local copy from a remote repository.

Common Misconceptions

"git push uploads my whole project"

No. git push only sends commits the remote doesn't already have. If you've made 100 commits and the remote has 98, push sends only the 2 new ones. Git is efficient—it transfers the minimum necessary data.

"Deleting a file locally deletes it from GitHub"

No. Deleting a file on your machine changes nothing on the remote. The deletion only reaches GitHub after you git add the deletion, git commit, and git push. Until then, the file still exists on GitHub exactly as it was.

TaskForge Connection

Push TaskForge to a GitHub repository. Create a .gitignore that excludes __pycache__/, .venv/, *.pyc, and .env. Create a feature branch, push it to GitHub, and open a pull request—even if you're the only reviewer. This is the same workflow you'll use when Claude Code creates branches and PRs for your review in Phase 4.

Micro-Exercises

1: Push TaskForge to GitHub

Create a new GitHub repository and push TaskForge to it:

git remote add origin https://github.com/YOUR-USERNAME/taskforge.git
git push -u origin main

Verify by visiting your GitHub repo URL—your files and commits should appear.

2: Add a .gitignore

Create a .gitignore file in your TaskForge project root:

__pycache__/
.venv/
*.pyc

Stage, commit, and push:

git add .gitignore
git commit -m "add .gitignore"
git push
3: Branch, Push, and PR

Create a branch, push it, and open a pull request on GitHub:

git switch -c add-delete-feature
# make a change (e.g., add a delete command to TaskForge)
git add .
git commit -m "add delete feature"
git push -u origin add-delete-feature

Then go to GitHub. Click "Compare & pull request." Write a description and submit. You now have a PR—even if you merge it yourself.

Try This Now

Fork a public Python repository on GitHub. Clone your fork. Create a branch, make a small improvement (fix a typo, improve a docstring), push your branch, and open a pull request.

Terminal# 1. Fork a repo on GitHub (click "Fork" on the repo page) # 2. Clone your fork git clone https://github.com/YOUR-USERNAME/forked-repo.git cd forked-repo # 3. Create a branch git switch -c fix-typo # 4. Make a small change (fix a typo, improve a docstring) # Edit a file with your editor # 5. Commit and push git add . git commit -m "fix typo in README" git push -u origin fix-typo # 6. Go to GitHub and open a pull request

Verification: You have a PR visible on GitHub with your changes.

If this doesn't work: (1) If push is rejected, try git pull --rebase origin main first. (2) If SSH fails, check ssh -T git@github.com to verify your key is set up. (3) If you can't find the fork button, make sure you're logged into GitHub and viewing someone else's public repository.

You just pushed code to GitHub, created a branch on a remote, and opened a pull request—the exact workflow that powers professional collaboration and AI-assisted development with Claude Code.
Reading Diffs: A Preview of AI Supervision

In Phase 5, you'll review git diff output from AI agents every day. The format below is the same one Git uses when you run git diff. Lines starting with + were added. Lines starting with - were removed. The @@ line shows where in the file the change happened. Practice reading this now—it's the same skill, just a different context.

Review an AI Agent's Diff

An AI agent was asked: "Add error handling to the delete_task function." Below is the diff it produced. Read it carefully, then write a function evaluate_diff() that returns a dict with three keys: "correct_changes" (list of line numbers with valid error-handling additions), "unrelated_change" (the line number of a change that wasn't requested), and "missing" (a string describing what the agent forgot to handle).

Lines with + are additions. The isinstance check is relevant error handling. But what about the list_tasks change?

The agent was only asked to add error handling to delete_task. Changing list_tasks is scope creep—an unrelated change.

What happens if tasks is None or not a list? The agent added a type check for task_id but forgot to validate tasks itself.

Interactive Exercises

Knowledge Check

What does the -u flag do in git push -u origin main?

Knowledge Check

What is the primary purpose of a pull request?

GitHub Workflow

Remote URL

Run git remote -v in your TaskForge repo and paste the output.

Phase 3 Gate Checkpoint & TaskForge on GitHub

Minimum Competency

Use terminal commands fluently, create git branches and resolve conflicts, push to a remote repository, and collaborate via pull requests.

Your Artifact

TaskForge pushed to GitHub with a .gitignore, at least 3 commits, and a feature branch merged via pull request.

Verification

Your TaskForge repo is visible on GitHub. git log --oneline shows 3+ commits. The merged PR is visible in the Pull Requests tab.

Failure Signal

If you can't push to GitHub or create a PR → return to Chapter 12.

What You Can Now Do

  • Navigate and compose terminal commands fluently
  • Use Git for version control: commits, branches, merges, conflict resolution
  • Push code to GitHub and pull remote changes
  • Collaborate through pull requests and code review
  • Keep repositories clean with .gitignore
Bridge to Phase 4

You now have the development tools that professional workflows depend on. Phase 4 builds on this foundation with APIs and HTTP, building APIs with Flask, database basics, Docker containers, CI/CD with GitHub Actions, and project architecture. You'll go from pushing code to GitHub to deploying full applications.