Explore project context

Check pxl-components library, data files, and any existing project structure

Offer visual companion

Offer the visual browser companion for UI mockups

Ask clarifying questions

One at a time, understand purpose, constraints, and success criteria

Propose 2-3 approaches

Present trade-offs and a recommendation

Present design

In sections scaled to complexity, get user approval after each section

Write design doc

Save to docs/superpowers/specs/YYYY-MM-DD-heapoverflow-design.md and commit

Spec self-review

Check for placeholders, contradictions, ambiguity, scope issues

User reviews written spec

Ask user to review spec file before proceeding

Transition to implementation

Invoke writing-plans skill to create implementation plan


Build Stack Overflow Teams clone with pxl-components


Conversation compacted


This session is being continued from a previous conversation that ran out of context. The summary below covers the earlier portion of the conversation.

Summary:
1. Primary Request and Intent:
   The user (referred to as "Sir Charles of St Cheese") wants to build **HeapOverflow** — an internal StackOverflow for Teams clone using:
   - The internal Angular UI library at `~/projects/pxl-components` (`@immuta/pxl-components` 21.0.5)
   - Existing exported data from `~/projects/heapoverflow/data/` (401 questions, 107 users, 117 tags, 2 articles)
   - A **Go REST API + Angular SPA** delivered as a **single deployable binary**
   - **PostgreSQL** as the database with a Docker bootstrap for local dev
   - **Okta OIDC** authentication + static test user fallback for local dev
   - Full CRUD for Q&A (questions, answers, comments), read-only articles for v1
   - Reputation/badges imported from export but earning logic deferred to v2

2. Key Technical Concepts:
   - Go 1.23.12, module path: `github.com/immuta/heapoverflow`
   - Angular CLI 21.2.0, standalone components, `@immuta/pxl-components` from private Immuta npm registry (`https://pkg-hub.infrastructure.immuta.io/repository/npm-hosted/`)
   - Angular dist output: `ui/dist/heapoverflow/browser/`
   - Go `//go:embed` to bundle Angular SPA into binary (`../../ui/dist/heapoverflow/browser`)
   - `pgx/v5` with `pgxpool` for PostgreSQL; `DBTX` interface satisfied by pool, conn, and tx
   - `golang-migrate/v4` with `iofs` source (embedded migrations)
   - `gorilla/sessions` CookieStore for HTTP-only session cookies
   - `go-oidc/v3` + `golang.org/x/oauth2` for Okta OIDC
   - `yuin/goldmark` for markdown→HTML rendering (GFM, XSS-safe — no `WithUnsafe`)
   - `go-playground/validator/v10` for input validation
   - Go 1.22+ `net/http.ServeMux` with method+path patterns (no gorilla/mux needed)
   - PostgreSQL `tsvector` GENERATED ALWAYS AS STORED columns + GIN indexes for full-text search
   - Votes tracked individually in `votes` table for new activity; imported counts cached as denormalized columns on posts
   - `legacy_id` nullable on all imported tables with `UNIQUE NULLS NOT DISTINCT` partial constraint
   - `body_html` pre-rendered at write time on questions, answers, comments, articles
   - Store layer integration tests use `TEST_DATABASE_URL` env var (skip if unset)
   - Polymorphic `post_id`/`post_type` pattern for comments and votes

3. Files and Code Sections:

   **`docs/superpowers/specs/2026-05-07-heapoverflow-design.md`**
   - Full design spec covering architecture, data model, REST API, auth, search, and Angular routes
   - Three spec fixes applied: nullable legacy_id note, body_html added to comments, Angular dist path corrected to `ui/dist/heapoverflow/browser/`

   **`docs/superpowers/plans/2026-05-07-heapoverflow-backend.md`**
   - 16-task TDD implementation plan for the Go backend
   - Covers project scaffold through server entrypoint
   - Complete code for every step

   **`~/.claude/settings.json`** (modified)
   - Added `CLAUDE_CODE_MAX_OUTPUT_TOKENS: "100000"` to `env` block to fix 32000 token output limit

   **Key code patterns in the backend plan:**

   Store DBTX interface (`internal/store/store.go`):
   ```go
   type DBTX interface {
       Exec(context.Context, string, ...any) (pgconn.CommandTag, error)
       Query(context.Context, string, ...any) (pgx.Rows, error)
       QueryRow(context.Context, string, ...any) pgx.Row
   }
   type Postgres struct{ db DBTX }
   func New(db DBTX) *Postgres { return &Postgres{db: db} }
   var _ Store = (*Postgres)(nil) // compile-time assertion
   ```

   Migration embed (`cmd/server/migrate.go`):
   ```go
   //go:embed ../../migrations
   var migrationsFS embed.FS
   func runMigrations(databaseURL string) error {
       src, _ := iofs.New(migrationsFS, "migrations")
       m, _ := migrate.NewWithSourceInstance("iofs", src, "pgx5://"+databaseURL[len("postgres://"):])
       defer m.Close()
       return m.Up() // ignores ErrNoChange
   }
   ```

   SPA fallback routing (`cmd/server/main.go`):
   ```go
   //go:embed ../../ui/dist/heapoverflow/browser
   var uiFS embed.FS
   // ... fs.Sub then:
   mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
       f, err := uiSubFS.Open(r.URL.Path)
       if err != nil {
           http.ServeFileFS(w, r, uiSubFS, "index.html") // Angular routing fallback
           return
       }
       f.Close()
       fileServer.ServeHTTP(w, r)
   }))
   ```

   Go 1.22+ routing (`internal/api/router.go`):
   ```go
   mux.HandleFunc("GET /api/v1/questions", h.handleListQuestions)
   mux.Handle("GET /api/v1/questions/{id}", auth(http.HandlerFunc(h.handleGetQuestion)))
   ```

   Vote cast with cached count update (`internal/store/votes.go`):
   ```go
   table := params.PostType + "s"
   p.db.Exec(ctx, fmt.Sprintf(`UPDATE %s SET
       up_vote_count = (SELECT COUNT(*) FROM votes WHERE post_id=$1 AND post_type=$2 AND vote_type=1),
       down_vote_count = (SELECT COUNT(*) FROM votes WHERE post_id=$1 AND post_type=$2 AND vote_type=-1),
       score = (SELECT COALESCE(SUM(vote_type),0) FROM votes WHERE post_id=$1 AND post_type=$2)
       WHERE id=$1`, table), params.PostID, params.PostType)
   ```

   Search using UNION ALL (`internal/store/search.go`):
   ```go
   SELECT 'question', id, title, ts_headline(...), tags, ts_rank(...), created_at
   FROM questions WHERE search_vector @@ plainto_tsquery('english', $1) AND is_deleted=FALSE
   UNION ALL
   SELECT 'article', ...FROM articles WHERE search_vector @@ plainto_tsquery('english', $1)
   ORDER BY rank DESC LIMIT 50
   ```

4. Errors and fixes:
   - **32000 output token limit**: The first attempt to write the implementation plan in a single response exceeded the output token maximum. Fixed by: (1) user ran `/update-config` to add `CLAUDE_CODE_MAX_OUTPUT_TOKENS: "100000"` to global settings, (2) user restarted Claude Code, (3) plan was written directly to file using `Write` tool instead of outputting in text response.
   - **golang-migrate embed path**: During plan writing, identified that the `//go:embed` path in `internal/db/db.go` would not work because `migrations/` is at repo root but `db.go` is in `internal/db/`. Fixed by moving migration embed to `cmd/server/migrate.go` where the relative path `../../migrations` resolves correctly.
   - **Spec review found 3 issues**: (1) legacy_id not documented as nullable → added note about `UNIQUE NULLS NOT DISTINCT`; (2) comments table missing `body_html` → added column; (3) Angular dist path wrong → corrected to `ui/dist/heapoverflow/browser/`.

5. Problem Solving:
   - Decomposed a large full-stack project into backend and frontend plans to stay within output limits
   - Used `Write` tool to write plan directly to file (avoiding response token limits)
   - Identified correct pgxpool→Store→DBTX wiring pattern for testability
   - Designed importer to handle the mismatch between SO export format (nested answers/comments, unix timestamps, legacy IDs) and the new schema
   - Handled the accepted_answer_id circular FK between questions and answers using `DEFERRABLE INITIALLY DEFERRED`

6. All user messages:
   - "I want to create a StackOverflow for Teams clone using our internal UI library at @~/projects/pxl-components and I have an export of currently existing data in @data that I will want to import"
   - "yes lets try it" (accepting visual companion)
   - "users should be able to post new questions, answers and comments"
   - "i would like to use a Go API and the angular front end and serve it as a single deployable binary"
   - "for authentication I would like to use oauth via okta but for testing we can have a static user"
   - "I would like to use PostgreSQL but for local development we should have a bootstrap that spins up a local (in docker) postgres instance and sets it up for use"
   - "lets keep it simpler for now but keep in mind that I do want to add badge earning and reputation (and import it from the users.json file) at a later time"
   - "option A" (selecting Go-first monorepo architecture)
   - "yes this looks right" (approving architecture section)
   - "yes this looks right" (approving data model section)
   - "looks right to me" (approving API and frontend routing section)
   - "this looks right" (approving UI layout section)
   - "looks good" (approving written spec)
   - "continue the implementation plan" (after restart, prompting frontend plan to be written)

7. Pending Tasks:
   - **Write the Angular frontend implementation plan** to `docs/superpowers/plans/2026-05-07-heapoverflow-frontend.md`
   - Tasks to cover in frontend plan:
     - Task 17: Angular scaffold (`ng new` in `ui/`, `.npmrc` for `@immuta` registry, pxl-components install, `proxy.conf.json`)
     - Task 18: Angular auth (AuthService, AuthGuard, auth interceptor with `withCredentials: true`, login component, auth-callback component, app.config.ts)
     - Task 19: Angular models and API services (TypeScript interfaces mirroring Go types, HttpClient services per domain)
     - Task 20: Angular layout shell (VerticalNav + PageHeader from pxl-components)
     - Task 21: Question list component (with sort chips, tag filter, pagination)
     - Task 22: Question detail component (vote controls, answer list, comment threads, accept answer)
     - Task 23: Ask question component (form with markdown textarea and tag input)
     - Task 24: Tags components (TagListComponent, TagDetailComponent with SMEs)
     - Task 25: Articles, users, and search components
     - Task 26: Build integration (final go:embed wiring, ng proxy config, Makefile `make build` and `make dev` verification)

8. Current Work:
   Immediately before this summary request, the backend plan was completed and written to `docs/superpowers/plans/2026-05-07-heapoverflow-backend.md` (16 tasks, ~1000 lines of detailed TDD steps with complete code). The session then received the summary request. The user's last message was "continue the implementation plan" after restarting Claude Code to apply the token limit fix. The backend plan was written successfully, and the next step is to write the frontend plan.

9. Optional Next Step:
   Write the Angular frontend implementation plan to `docs/superpowers/plans/2026-05-07-heapoverflow-frontend.md`.

   The plan's last line states: "Continue with `2026-05-07-heapoverflow-frontend.md` for the Angular frontend tasks."

   This frontend plan needs to cover Angular scaffold, authentication, models/services, layout shell, all feature components (questions, tags, articles, users, search), and the final build integration that wires the Angular dist into the Go embed. This directly continues the writing-plans skill invocation that was in progress (Task 9: "Transition to implementation" is still `in_progress`).

If you need specific details from before compaction (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/steve.briggs/.claude/projects/-Users-steve-briggs-projects-heapoverflow/184fce80-66ab-4333-aea0-945736ac83dc.jsonl
Continue the conversation from where it left off without asking the user any further questions. Resume directly — do not acknowledge the summary, do not recap what was happening, do not preface with "I'll continue" or similar. Pick up the last task as if the break never happened.


afplay ~/.claude/sounds/mario/prompt-submit/coin.wav 2>/dev/null || true

afplay ~/.claude/sounds/mario/stop/stomp.wav 2>/dev/null || true

afplay ~/.claude/sounds/mario/pause.wav 2>/dev/null || true

afplay ~/.claude/sounds/mario/tool-failure/bowser-fire.wav 2>/dev/null || true


# HeapOverflow — Design Spec

**Date:** 2026-05-07
**Status:** Approved

## Overview

HeapOverflow is an internal StackOverflow for Teams clone. It imports an existing SO Teams export (401 questions, 107 users, 117 tags, 2 articles) and allows users to continue posting questions, answers, and comments. It is distributed as a single Go binary that embeds the Angular frontend.

---

## Architecture

### Repo Structure

```
heapoverflow/
├── cmd/
│   ├── server/main.go       ← HTTP server entrypoint
│   └── importer/main.go     ← JSON import CLI
├── internal/
│   ├── api/                 ← REST handlers
│   ├── auth/                ← Okta OIDC + static user
│   ├── store/               ← DB queries (pgx)
│   └── importer/            ← JSON import logic
├── migrations/              ← SQL migration files (sequential numbered)
├── ui/                      ← Angular app (consumes pxl-components)
├── scripts/
│   └── bootstrap.sh         ← spins up Docker postgres, runs migrations, imports data
├── Makefile
└── go.mod
```

### Runtime

- **Single binary**: Go server embeds `ui/dist/heapoverflow/browser/**` via `//go:embed`. Requests to `/api/v1/*` are handled by REST handlers; all other requests serve the embedded Angular SPA.
- **Database**: PostgreSQL. Docker container for local dev; managed instance for production.
- **Auth**: Okta OIDC for production. Static test user (toggled by `AUTH_MODE=static`) for local development.

### Dev Workflow

```
make dev
```
1. `scripts/bootstrap.sh` — starts Docker Postgres, runs migrations, imports JSON data (idempotent)
2. `go run ./cmd/server` — API on `:8080`
3. `ng serve` — Angular on `:4200`, proxies `/api` → `:8080`

### Build Workflow

```
make build
```
1. `ng build --configuration production` → `ui/dist/heapoverflow/browser/`
2. `go build ./cmd/server` — embeds `ui/dist/heapoverflow/browser/`, produces `./heapoverflow` binary
3. Binary reads all config from environment variables

---

## Data Model

### Tables

**users**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| user_id | INT UNIQUE | SO Teams export id |
| account_id | INT UNIQUE | SO account id |
| okta_id | TEXT UNIQUE | set on first Okta login |
| display_name | TEXT | |
| email | TEXT UNIQUE | |
| profile_image | TEXT | |
| about_me | TEXT | |
| reputation | INT | imported; earning logic in v2 |
| badge_bronze | INT | imported; earning logic in v2 |
| badge_silver | INT | imported; earning logic in v2 |
| badge_gold | INT | imported; earning logic in v2 |
| is_employee | BOOL | |
| created_at | TIMESTAMPTZ | |
| last_access_at | TIMESTAMPTZ | |

**questions**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| legacy_id | INT UNIQUE | SO question_id |
| user_id | INT FK→users | |
| title | TEXT | |
| body_markdown | TEXT | |
| body_html | TEXT | pre-rendered at write time |
| is_answered | BOOL | |
| is_deleted | BOOL | |
| is_obsolete | BOOL | |
| view_count | INT | |
| score | INT | |
| up_vote_count | INT | cached; updated when votes table changes |
| down_vote_count | INT | cached; updated when votes table changes |
| accepted_answer_id | INT FK→answers | nullable |
| search_vector | TSVECTOR | maintained by trigger, GIN indexed |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

**answers**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| legacy_id | INT UNIQUE | |
| question_id | INT FK→questions | |
| user_id | INT FK→users | |
| body_markdown | TEXT | |
| body_html | TEXT | pre-rendered at write time |
| is_accepted | BOOL | |
| score | INT | |
| up_vote_count | INT | cached |
| down_vote_count | INT | cached |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

**comments**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| user_id | INT FK→users | |
| post_id | INT | polymorphic |
| post_type | TEXT | `'question'` or `'answer'` |
| body_markdown | TEXT | |
| body_html | TEXT | pre-rendered at write time |
| created_at | TIMESTAMPTZ | |

**tags**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| legacy_id | INT UNIQUE | |
| name | TEXT UNIQUE | |
| description | TEXT | |
| count | INT | |
| is_moderator_only | BOOL | |
| is_required | BOOL | |

**votes**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| user_id | INT FK→users | |
| post_id | INT | polymorphic |
| post_type | TEXT | `'question'` or `'answer'` |
| vote_type | SMALLINT | `1` (up) or `-1` (down) |
| created_at | TIMESTAMPTZ | |
| | UNIQUE | `(post_id, post_type, user_id)` — one vote per user per post |

**articles**
| Column | Type | Notes |
|--------|------|-------|
| id | SERIAL PK | |
| legacy_id | INT UNIQUE | |
| user_id | INT FK→users | |
| title | TEXT | |
| body_markdown | TEXT | |
| body_html | TEXT | pre-rendered at write time |
| article_type | TEXT | e.g. `knowledge-article` |
| view_count | INT | |
| score | INT | |
| search_vector | TSVECTOR | GIN indexed |
| created_at | TIMESTAMPTZ | |
| updated_at | TIMESTAMPTZ | |

**Note on `legacy_id`**: Present on all imported tables (`users`, `questions`, `answers`, `tags`, `articles`). Always nullable — only set for records created via the JSON import. The UNIQUE constraint is `UNIQUE WHERE legacy_id IS NOT NULL`. New records created by users leave `legacy_id` as NULL.

**Junction tables**: `question_tags (question_id, tag_id)`, `article_tags (article_id, tag_id)`, `tag_synonyms (tag_id, synonym TEXT)`, `tag_smes (tag_id, user_id)`.

### Import Strategy

`make import` runs `go run ./cmd/importer`, loading all JSON files in dependency order:

```
users → tags → questions → answers → comments → articles
```

Records are upserted by `legacy_id` — the import is idempotent and safe to re-run. Reputation, badge counts, and cached vote counts are imported as-is from `users.json`. The `votes` table is not populated from the import (only totals are available in the export); new votes after import are tracked individually.

---

## REST API

Base path: `/api/v1`

### Auth
| Method | Path | Description |
|--------|------|-------------|
| GET | `/auth/login` | Redirect to Okta authorization URL |
| GET | `/auth/callback` | Exchange code, set HTTP-only session cookie |
| POST | `/auth/logout` | Clear session |
| GET | `/auth/me` | Return current authenticated user |

### Questions
| Method | Path | Description |
|--------|------|-------------|
| GET | `/questions` | List questions. Query params: `page`, `sort` (newest/active/votes/unanswered), `tag`, `q` |
| POST | `/questions` | Create question |
| GET | `/questions/:id` | Get question with answers and comments |
| PUT | `/questions/:id` | Edit question (author or admin) |
| DELETE | `/questions/:id` | Soft-delete question (sets `is_deleted=true`) |
| POST | `/questions/:id/vote` | Cast or retract vote. Body: `{"vote_type": 1}` or `{"vote_type": -1}` |

### Answers
| Method | Path | Description |
|--------|------|-------------|
| POST | `/questions/:id/answers` | Post answer |
| PUT | `/answers/:id` | Edit answer (author or admin) |
| DELETE | `/answers/:id` | Delete answer |
| POST | `/answers/:id/vote` | Cast or retract vote |
| POST | `/answers/:id/accept` | Accept answer (question author only) |

### Comments
| Method | Path | Description |
|--------|------|-------------|
| POST | `/questions/:id/comments` | Add comment to question |
| POST | `/answers/:id/comments` | Add comment to answer |
| DELETE | `/comments/:id` | Delete comment (author or admin) |

### Tags
| Method | Path | Description |
|--------|------|-------------|
| GET | `/tags` | List tags. Query param: `q` (search by name) |
| GET | `/tags/:name` | Get tag detail with SMEs and question count |

### Users
| Method | Path | Description |
|--------|------|-------------|
| GET | `/users/:id` | Get user profile |
| PUT | `/users/me` | Update own profile |

### Articles
| Method | Path | Description |
|--------|------|-------------|
| GET | `/articles` | List articles |
| GET | `/articles/:id` | Get article detail |

### Search
| Method | Path | Description |
|--------|------|-------------|
| GET | `/search` | Full-text search. Query param: `q`. Returns ranked questions + articles |

---

## Authentication

### Okta OIDC (production)
1. Angular `AuthGuard` redirects unauthenticated users to `/api/v1/auth/login`
2. Go redirects to Okta authorization URL
3. Okta redirects to `/api/v1/auth/callback` with authorization code
4. Go exchanges code for tokens, looks up or creates user record (matching on `okta_id` / email), sets an **HTTP-only session cookie**
5. Go redirects to Angular `/auth/callback`; Angular calls `/auth/me` to hydrate current user state

### Static user (local dev / testing)
- Enabled when `AUTH_MODE=static` is set (automatically disabled when `OKTA_*` env vars are present)
- Login page shows a **"Dev Login"** button that sets a session as a pre-configured test user from the database
- No Okta credentials required for local development

---

## Search

Full-text search is implemented using PostgreSQL's built-in `tsvector`:
- A `search_vector` column on `questions` and `articles` is maintained by a database trigger
- Indexed with a GIN index for fast lookups
- `GET /search?q=` queries both tables, ranks results by relevance, and returns a merged list
- Sufficient for 400+ questions without requiring an external search service

---

## Frontend (Angular)

The Angular app lives in `ui/` and imports components from the `pxl-components` library.

### Routes
| Path | Component | Guard |
|------|-----------|-------|
| `/login` | LoginComponent | — |
| `/auth/callback` | AuthCallbackComponent | — |
| `/` | redirect → `/questions` | AuthGuard |
| `/questions` | QuestionListComponent | AuthGuard |
| `/questions/ask` | AskQuestionComponent | AuthGuard |
| `/questions/:id` | QuestionDetailComponent | AuthGuard |
| `/tags` | TagListComponent | AuthGuard |
| `/tags/:name` | TagDetailComponent | AuthGuard |
| `/articles` | ArticleListComponent | AuthGuard |
| `/articles/:id` | ArticleDetailComponent | AuthGuard |
| `/users/:id` | UserProfileComponent | AuthGuard |
| `/search` | SearchResultsComponent | AuthGuard |

### pxl-components Usage
- `VerticalNav` — sidebar navigation
- `PageHeader` — top navigation bar with search
- `SearchInput` — global search field
- `Breadcrumbs` — question/article detail pages
- `Chips` + `ChipFilter` — sort tabs and tag filters
- `Cards` — question list rows
- `Alert` — error and notice banners
- `SkeletonLoader` — loading states
- `FormField` — ask question / post answer forms
- `EmptyState` — no results views
- `StatusIndicator` — answered/unanswered badge
- `Tooltip` — vote count details

---

## Out of Scope (v1)

The following are explicitly deferred to future iterations:

- **Badge earning** — badge counts are imported and displayed but not earned through new activity
- **Reputation changes** — reputation is imported as a static value; delta tracking is a v2 concern
- **Article creation/editing** — articles are read-only in v1 (imported only)
- **Admin UI** — moderation is handled directly in the database or via future work
- **Email notifications** — no notification system in v1


UI Layout

Section 4 of 4 — page structure using pxl-components. Does this feel right?

Questions List (/questions)

⬡ HeapOverflow

🔍 Search...

👤 Steve

Questions

Tags

Articles

Users

All Questions

Ask Question

Newest

Active

Votes

Unanswered

12

votes

3

ans ✓

How do we handle k8s pod restarts in SRE runbooks?

                    k8s
                    sre

asked by Floyd H. · 2d ago

3

votes

0

ans

Best practices for bodata-development migrations?

                    bodata-development

asked by Barry H. · 5h ago

7

votes

1

ans ✓

SaaS deployment checklist for new regions?

                    saas

asked by Jeff H. · 1w ago

1

2

3

›

Question Detail (/questions/:id)

⬡ HeapOverflow

🔍 Search...

👤 Steve

Questions

Tags

Articles

Users

How do we handle k8s pod restarts in SRE runbooks?

              k8s
              sre
              asked by Floyd H. · 2d ago · 45 views

▲

12

▼

                We've been seeing issues with our SRE runbooks not properly accounting for pod restart scenarios...

2 comments

3 Answers

▲

8

▼

✓

                The key is to add a livenessProbe with appropriate thresholds...

answered by Haris K. · accepted ✓

Your Answer

Post Answer

pxl-components Mapping

Navigation

VerticalNav — sidebar

PageHeader — top bar

Breadcrumbs — question detail

SearchInput — global search

Content

Chips — sort tabs & tags

Cards — question rows

Alert — errors / notices

SkeletonLoader — loading state

Forms & Feedback

FormField — ask/answer forms

EmptyState — no results

StatusIndicator — answered badge

Tooltip — vote counts


API & Frontend Routes

Section 3 of 4 — REST endpoints and Angular page structure

REST API  base: /api/v1

AUTH

GET  /auth/login → redirect to Okta

GET  /auth/callback → exchange code, set session

POST /auth/logout

GET  /auth/me → current user

QUESTIONS

GET  /questions ?page &sort &tag &q

POST /questions

GET  /questions/:id

PUT  /questions/:id

DELETE /questions/:id

POST /questions/:id/vote

ANSWERS

POST /questions/:id/answers

PUT  /answers/:id

DELETE /answers/:id

POST /answers/:id/vote

POST /answers/:id/accept

COMMENTS

POST /questions/:id/comments

POST /answers/:id/comments

DELETE /comments/:id

TAGS

GET  /tags ?q (search)

GET  /tags/:name → tag + SMEs

USERS

GET  /users/:id

PUT  /users/me

ARTICLES

GET  /articles

GET  /articles/:id

SEARCH

GET  /search ?q → questions + articles

Angular Routes

PUBLIC

/login → LoginComponent

/auth/callback → AuthCallbackComponent

AUTHENTICATED (AuthGuard)

/ → redirect to /questions

/questions → QuestionListComponent

sort: newest | active | votes | unanswered

filter by tag, full-text search

/questions/ask → AskQuestionComponent

/questions/:id → QuestionDetailComponent

answers, comments, voting, accept

/tags → TagListComponent

/tags/:name → TagDetailComponent

SMEs, questions with this tag

/articles → ArticleListComponent

/articles/:id → ArticleDetailComponent

/users/:id → UserProfileComponent

reputation, badges, activity

/search → SearchResultsComponent

Auth Flow

OKTA (production)

1. Angular AuthGuard → redirect to /api/v1/auth/login

2. Go redirects → Okta authorization URL

3. Okta callback → /api/v1/auth/callback

4. Go exchanges code, looks up or creates user, sets HTTP-only session cookie

5. Redirect to Angular /auth/callback → Angular reads /auth/me → proceed

STATIC USER (dev / testing)

Enabled by env var AUTH_MODE=static

Login page shows a "Dev Login" button

Go sets session as a pre-configured test user

No Okta credentials required locally

Static mode disabled if OKTA_* env vars present

Search Strategy

      Full-text search powered by PostgreSQL's built-in tsvector — no external service needed.
      A search_vector column on questions and articles is maintained via a trigger,
      indexed with GIN. The GET /search?q= endpoint queries both tables and returns a ranked, merged list.
      Good enough for 400+ questions without Elasticsearch overhead.


## claude.ai Context7
Use this server to fetch current documentation whenever the user asks about a library, framework, SDK, API, CLI tool, or cloud service -- even well-known ones like React, Next.js, Prisma, Express, Tailwind, Django, or Spring Boot. This includes API syntax, configuration, version migration, library-specific debugging, setup instructions, and CLI tool usage. Use even when you think you know the answer -- your training data may not reflect recent changes. Prefer this over web search for library docs.

Do not use for: refactoring, writing scripts from scratch, debugging business logic, code review, or general programming concepts.


<memvid-mind-context>
# 🧠 Claude Mind Active

📁 Project: **heapoverflow**
💾 Memory: `.claude/mind.mv2` (4480 KB)

**Commands:**
- `/mind:search <query>` - Search memories
- `/mind:ask <question>` - Ask your memory
- `/mind:recent` - View timeline
- `/mind:stats` - View statistics

_Memories are captured automatically from your tool use._
</memvid-mind-context>


<EXTREMELY_IMPORTANT>
You have superpowers.

**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**

---
name: using-superpowers
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
---

<SUBAGENT-STOP>
If you were dispatched as a subagent to execute a specific task, skip this skill.
</SUBAGENT-STOP>

<EXTREMELY-IMPORTANT>
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.

IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.

This is not negotiable. This is not optional. You cannot rationalize your way out of this.
</EXTREMELY-IMPORTANT>

## Instruction Priority

Superpowers skills override default system prompt behavior, but **user instructions always take precedence**:

1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
2. **Superpowers skills** — override default system behavior where they conflict
3. **Default system prompt** — lowest priority

If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.

## How to Access Skills

**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.

**In Copilot CLI:** Use the `skill` tool. Skills are auto-discovered from installed plugins. The `skill` tool works the same as Claude Code's `Skill` tool.

**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.

**In other environments:** Check your platform's documentation for how skills are loaded.

## Platform Adaptation

Skills use Claude Code tool names. Non-CC platforms: see `references/copilot-tools.md` (Copilot CLI), `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.

# Using Skills

## The Rule

**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.

```dot
digraph skill_flow {
    "User message received" [shape=doublecircle];
    "About to EnterPlanMode?" [shape=doublecircle];
    "Already brainstormed?" [shape=diamond];
    "Invoke brainstorming skill" [shape=box];
    "Might any skill apply?" [shape=diamond];
    "Invoke Skill tool" [shape=box];
    "Announce: 'Using [skill] to [purpose]'" [shape=box];
    "Has checklist?" [shape=diamond];
    "Create TodoWrite todo per item" [shape=box];
    "Follow skill exactly" [shape=box];
    "Respond (including clarifications)" [shape=doublecircle];

    "About to EnterPlanMode?" -> "Already brainstormed?";
    "Already brainstormed?" -> "Invoke brainstorming skill" [label="no"];
    "Already brainstormed?" -> "Might any skill apply?" [label="yes"];
    "Invoke brainstorming skill" -> "Might any skill apply?";

    "User message received" -> "Might any skill apply?";
    "Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
    "Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
    "Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
    "Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
    "Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
    "Has checklist?" -> "Follow skill exactly" [label="no"];
    "Create TodoWrite todo per item" -> "Follow skill exactly";
}
```

## Red Flags

These thoughts mean STOP—you're rationalizing:

| Thought | Reality |
|---------|---------|
| "This is just a simple question" | Questions are tasks. Check for skills. |
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
| "Let me gather information first" | Skills tell you HOW to gather information. |
| "This doesn't need a formal skill" | If a skill exists, use it. |
| "I remember this skill" | Skills evolve. Read current version. |
| "This doesn't count as a task" | Action = task. Check for skills. |
| "The skill is overkill" | Simple things become complex. Use it. |
| "I'll just do this one thing first" | Check BEFORE doing anything. |
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |

## Skill Priority

When multiple skills could apply, use this order:

1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task
2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution

"Let's build X" → brainstorming first, then implementation skills.
"Fix this bug" → debugging first, then domain-specific skills.

## Skill Types

**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline.

**Flexible** (patterns): Adapt principles to context.

The skill itself tells you which.

## User Instructions

Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.

</EXTREMELY_IMPORTANT>


<memvid-mind-context>
# 🧠 Claude Mind Active

📁 Project: **heapoverflow**
💾 Memory: `.claude/mind.mv2` (4480 KB)

**Commands:**
- `/mind:search <query>` - Search memories
- `/mind:ask <question>` - Ask your memory
- `/mind:recent` - View timeline
- `/mind:stats` - View statistics

_Memories are captured automatically from your tool use._
</memvid-mind-context>

<EXTREMELY_IMPORTANT>
You have superpowers.

**Below is the full content of your 'superpowers:using-superpowers' skill - your introduction to using skills. For all other skills, use the 'Skill' tool:**

---
name: using-superpowers
description: Use when starting any conversation - establishes how to find and use skills, requiring Skill tool invocation before ANY response including clarifying questions
---

<SUBAGENT-STOP>
If you were dispatched as a subagent to execute a specific task, skip this skill.
</SUBAGENT-STOP>

<EXTREMELY-IMPORTANT>
If you think there is even a 1% chance a skill might apply to what you are doing, you ABSOLUTELY MUST invoke the skill.

IF A SKILL APPLIES TO YOUR TASK, YOU DO NOT HAVE A CHOICE. YOU MUST USE IT.

This is not negotiable. This is not optional. You cannot rationalize your way out of this.
</EXTREMELY-IMPORTANT>

## Instruction Priority

Superpowers skills override default system prompt behavior, but **user instructions always take precedence**:

1. **User's explicit instructions** (CLAUDE.md, GEMINI.md, AGENTS.md, direct requests) — highest priority
2. **Superpowers skills** — override default system behavior where they conflict
3. **Default system prompt** — lowest priority

If CLAUDE.md, GEMINI.md, or AGENTS.md says "don't use TDD" and a skill says "always use TDD," follow the user's instructions. The user is in control.

## How to Access Skills

**In Claude Code:** Use the `Skill` tool. When you invoke a skill, its content is loaded and presented to you—follow it directly. Never use the Read tool on skill files.

**In Copilot CLI:** Use the `skill` tool. Skills are auto-discovered from installed plugins. The `skill` tool works the same as Claude Code's `Skill` tool.

**In Gemini CLI:** Skills activate via the `activate_skill` tool. Gemini loads skill metadata at session start and activates the full content on demand.

**In other environments:** Check your platform's documentation for how skills are loaded.

## Platform Adaptation

Skills use Claude Code tool names. Non-CC platforms: see `references/copilot-tools.md` (Copilot CLI), `references/codex-tools.md` (Codex) for tool equivalents. Gemini CLI users get the tool mapping loaded automatically via GEMINI.md.

# Using Skills

## The Rule

**Invoke relevant or requested skills BEFORE any response or action.** Even a 1% chance a skill might apply means that you should invoke the skill to check. If an invoked skill turns out to be wrong for the situation, you don't need to use it.

```dot
digraph skill_flow {
    "User message received" [shape=doublecircle];
    "About to EnterPlanMode?" [shape=doublecircle];
    "Already brainstormed?" [shape=diamond];
    "Invoke brainstorming skill" [shape=box];
    "Might any skill apply?" [shape=diamond];
    "Invoke Skill tool" [shape=box];
    "Announce: 'Using [skill] to [purpose]'" [shape=box];
    "Has checklist?" [shape=diamond];
    "Create TodoWrite todo per item" [shape=box];
    "Follow skill exactly" [shape=box];
    "Respond (including clarifications)" [shape=doublecircle];

    "About to EnterPlanMode?" -> "Already brainstormed?";
    "Already brainstormed?" -> "Invoke brainstorming skill" [label="no"];
    "Already brainstormed?" -> "Might any skill apply?" [label="yes"];
    "Invoke brainstorming skill" -> "Might any skill apply?";

    "User message received" -> "Might any skill apply?";
    "Might any skill apply?" -> "Invoke Skill tool" [label="yes, even 1%"];
    "Might any skill apply?" -> "Respond (including clarifications)" [label="definitely not"];
    "Invoke Skill tool" -> "Announce: 'Using [skill] to [purpose]'";
    "Announce: 'Using [skill] to [purpose]'" -> "Has checklist?";
    "Has checklist?" -> "Create TodoWrite todo per item" [label="yes"];
    "Has checklist?" -> "Follow skill exactly" [label="no"];
    "Create TodoWrite todo per item" -> "Follow skill exactly";
}
```

## Red Flags

These thoughts mean STOP—you're rationalizing:

| Thought | Reality |
|---------|---------|
| "This is just a simple question" | Questions are tasks. Check for skills. |
| "I need more context first" | Skill check comes BEFORE clarifying questions. |
| "Let me explore the codebase first" | Skills tell you HOW to explore. Check first. |
| "I can check git/files quickly" | Files lack conversation context. Check for skills. |
| "Let me gather information first" | Skills tell you HOW to gather information. |
| "This doesn't need a formal skill" | If a skill exists, use it. |
| "I remember this skill" | Skills evolve. Read current version. |
| "This doesn't count as a task" | Action = task. Check for skills. |
| "The skill is overkill" | Simple things become complex. Use it. |
| "I'll just do this one thing first" | Check BEFORE doing anything. |
| "This feels productive" | Undisciplined action wastes time. Skills prevent this. |
| "I know what that means" | Knowing the concept ≠ using the skill. Invoke it. |

## Skill Priority

When multiple skills could apply, use this order:

1. **Process skills first** (brainstorming, debugging) - these determine HOW to approach the task
2. **Implementation skills second** (frontend-design, mcp-builder) - these guide execution

"Let's build X" → brainstorming first, then implementation skills.
"Fix this bug" → debugging first, then domain-specific skills.

## Skill Types

**Rigid** (TDD, debugging): Follow exactly. Don't adapt away discipline.

**Flexible** (patterns): Adapt principles to context.

The skill itself tells you which.

## User Instructions

Instructions say WHAT, not HOW. "Add X" or "Fix Y" doesn't mean skip workflows.

</EXTREMELY_IMPORTANT>


1	# HeapOverflow Backend Implementation Plan
2
3	> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
5	**Goal:** Build the Go API backend for HeapOverflow — a StackOverflow for Teams clone that serves as a single binary embedding the Angular frontend.
6
7	**Architecture:** Go 1.23 REST API using stdlib `net/http` (Go 1.22+ routing), `pgx/v5` for PostgreSQL, `gorilla/sessions` for HTTP-only cookie sessions, and `//go:embed` to bundle the Angular dist. The importer CLI is a separate binary that reads the 5 JSON export files and upserts records into PostgreSQL.
8
9	**Tech Stack:** Go 1.23.12 · PostgreSQL 16 · pgx/v5 · golang-migrate/v4 · gorilla/sessions · go-oidc/v3 · goldmark · go-playground/validator/v10 · testify
10
11	---
12
13	## Task 1: Project Scaffold
14
15	**Files:**
16	- Create: `go.mod`
17	- Create: `Makefile`
18	- Create: `.env.example`
19	- Create: `docker-compose.yml`
20	- Create: `.gitignore`
21
22	- [ ] **Step 1: Initialize Go module**
23
24	```bash
25	cd /Users/steve.briggs/projects/heapoverflow
26	go mod init github.com/immuta/heapoverflow
27	```
28
29	Expected: `go.mod` created with `module github.com/immuta/heapoverflow` and `go 1.23`.
30

# HeapOverflow Backend Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Build the Go API backend for HeapOverflow — a StackOverflow for Teams clone that serves as a single binary embedding the Angular frontend.

**Architecture:** Go 1.23 REST API using stdlib `net/http` (Go 1.22+ routing), `pgx/v5` for PostgreSQL, `gorilla/sessions` for HTTP-only cookie sessions, and `//go:embed` to bundle the Angular dist. The importer CLI is a separate binary that reads the 5 JSON export files and upserts records into PostgreSQL.

**Tech Stack:** Go 1.23.12 · PostgreSQL 16 · pgx/v5 · golang-migrate/v4 · gorilla/sessions · go-oidc/v3 · goldmark · go-playground/validator/v10 · testify

---

## Task 1: Project Scaffold

**Files:**
- Create: `go.mod`
- Create: `Makefile`
- Create: `.env.example`
- Create: `docker-compose.yml`
- Create: `.gitignore`

- [ ] **Step 1: Initialize Go module**

```bash
cd /Users/steve.briggs/projects/heapoverflow
go mod init github.com/immuta/heapoverflow
```

Expected: `go.mod` created with `module github.com/immuta/heapoverflow` and `go 1.23`.


Writing the Angular frontend plan now.