Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .claude/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Pick the entry point matching the user's intent. Only **one** entry point runs p
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| [implement-task](implement-task/SKILL.md) | The default entry point for substantive work — scope, implement, self-check, test, docs, commit, review prompt. |
| [start-task](start-task/SKILL.md) | Lightweight scoping-only entry point. Reading order + alignment check. |
| [write-adr](write-adr/SKILL.md) | Capturing a **Hub-internal** decision (`HUB-NNNN` series). Cross-cutting decisions go in `../LearnStack/docs/decisions/`. |
| [write-adr](write-adr/SKILL.md) | Capturing a **Hub-internal** decision (`HUB-NNNN` series). Cross-cutting decisions go in [LearnStack's `docs/decisions/`](https://github.com/HodeTech/LearnStack/blob/main/docs/decisions/). |
| [update-glossary](update-glossary/SKILL.md) | Introducing a Hub-specific term in `docs/glossary.md`. |
| [commit-and-pr](commit-and-pr/SKILL.md) | Conventional Commit + AI trailer + Hub PR conventions (incl. cross-repo coordination). |

Expand Down Expand Up @@ -81,10 +81,12 @@ These Hub workflows aren't needed yet; create the skill when the packet that nee

| Skill | Owning packet | Mirror of |
| -------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------- |
| `add-provider-adapter` | P02c-2 (`LearnStackApiClient`) / P02c-5 (Let's Encrypt) / Phase 09b (Stripe, Iyzico) | LearnStack `add-provider-adapter` |
| `add-provider-adapter` | P02c-2 (`LearnStackApiClient`) / P02c-5 (Let's Encrypt) / Phase 09b (Stripe, Iyzico) | [LearnStack `add-provider-adapter`](https://github.com/HodeTech/LearnStack/blob/main/.claude/skills/add-provider-adapter/SKILL.md) |
| `add-hub-permission` | P02c-4 (Operators module) | LearnStack `add-permission` (operator-scope only) |
| `add-audit-coverage` | P02c-4 (Audit module) | LearnStack `add-audit-coverage` |
| `add-operator-portal-route` / `add-i18n-key` | P02c-4 (operator portal) | LearnStack `add-frontend-route` / `add-i18n-key` |
| `add-recurring-job` | P02c-5 (challenge verification + renewal) / Hub Billing (usage rollup) | **No LearnStack mirror — Hub-originated.** The scheduler choice is an open `HUB-NNNN` ADR |
| `add-contract-test` | P02c-2 (`entitlement-v1.schema.json` + the OpenAPI/SDK contract) | **No LearnStack mirror.** Covers the byte-identical-across-repos rule and how it is checked in a coordinated PR |

## Authoring a new skill

Expand All @@ -103,5 +105,5 @@ Body structure: **Purpose**, **When to use** / **When not to use**, **Inputs**,
## What skills are not

- **Not duplicates of standards.** A skill is a _workflow_. LearnStack's [Standards corpus](https://github.com/HodeTech/LearnStack/blob/main/docs/standards/) is the authority; skills cite it.
- **Not decisions.** Decisions live in ADRs (LearnStack `../LearnStack/docs/decisions/` for cross-cutting; this repo's `docs/decisions/` for `HUB-NNNN`).
- **Not decisions.** Decisions live in ADRs ([LearnStack's `docs/decisions/`](https://github.com/HodeTech/LearnStack/blob/main/docs/decisions/) for cross-cutting; this repo's `docs/decisions/` for `HUB-NNNN`).
- **Not scratch space.** Exploratory notes go in `docs/analysis/` (gitignored).
2 changes: 1 addition & 1 deletion .claude/skills/add-feature-key/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static readonly LimitKey <Name> = new("limits.<dotted.snake_case>");

### Step 3 — Keep it aligned with LearnStack core

The wire-format string **must** match LearnStack core's `FeatureKeys`/`LimitKeys` registry (`../LearnStack/backend/src/LearnStack.SharedKernel/FeatureFlags/`) exactly — LearnStack core reads the key from the projection by string. A mismatch means LearnStack silently never sees the feature. If you add a key Hub authors but LearnStack core doesn't yet read, note the pending LearnStack-side addition (a cross-repo registry-sync follow-up; the durable fix is a shared `LearnStack.Contracts` package, Phase 11 — see [plans.md § Registry sync](../../../docs/modules/plans.md)).
The wire-format string **must** match LearnStack core's `FeatureKeys` / `LimitKeys` registry exactly. That registry does **not exist yet** — it lands with [LearnStack Phase 02a Packet 9](https://github.com/HodeTech/LearnStack/blob/main/docs/roadmap/phase-02a-kernel-tenancy.md), so until then the default action is to record the pending LearnStack-side addition rather than to cross-check. When it exists it will live under [`https://github.com/HodeTech/LearnStack/blob/main/backend/src/LearnStack.SharedKernel/`](https://github.com/HodeTech/LearnStack/blob/main/backend/src/LearnStack.SharedKernel/) — LearnStack core reads the key from the projection by string. A mismatch means LearnStack silently never sees the feature. If you add a key Hub authors but LearnStack core doesn't yet read, note the pending LearnStack-side addition (a cross-repo registry-sync follow-up; the durable fix is a shared `LearnStack.Contracts` package, Phase 11 — see [plans.md § Registry sync](../../../docs/modules/plans.md)).

### Step 4 — Plan validator

Expand Down
14 changes: 10 additions & 4 deletions .claude/skills/add-integration-event/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ description: >
learnstack.hub.custom-domain.activated), wiring a module to publish it via
IOutbox.EnqueueAsync, or consuming a LearnStack-emitted event (learnstack.tenancy.*).
DO NOT USE FOR: intra-module domain events (plain MediatR INotification, in-process),
the four HTTPS contract endpoints (those are request/response, not pub/sub), or
adding a fifth HTTPS contract endpoint (needs an ADR).
the HTTPS contract surface (those are request/response, not pub/sub), or adding an
endpoint to it (needs a LearnStack ADR — the surface is governed by ADR-0034's two
invariants, not by a count).
---

# Adding a Hub integration event
Expand All @@ -29,7 +30,9 @@ Wire a cross-boundary event the right way: outbox-written in the same transactio
## When not to use

- Intra-module notification → plain MediatR `INotification` (`IDomainEvent`), in-process, same transaction. No outbox.
- A request/response contract → that's one of the four HTTPS endpoints (a fifth needs an ADR in `../LearnStack/docs/decisions/`).
- A request/response contract → that belongs to the HTTPS contract surface enumerated in
[ADR-0034 § The endpoint set](https://github.com/HodeTech/LearnStack/blob/main/docs/decisions/0034-hub-contract-surface-invariant.md);
adding one needs a LearnStack ADR.

## Workflow

Expand Down Expand Up @@ -80,4 +83,7 @@ For a LearnStack-emitted event Hub consumes (e.g. `learnstack.tenancy.tenant.ren
- **An unversioned event.** Breaking changes need `V2`.
- **Wrong topic prefix.** Hub publishes under `learnstack.hub.*`; never under bare `learnstack.*`.
- **A consumer without the inbox guard.** Re-delivery duplicates the side effect.
- **Reaching for a fifth HTTPS endpoint instead of an event.** If it's fire-and-forget cross-boundary state, it's an event; a new request/response endpoint needs an ADR.
- **Reaching for a new HTTPS endpoint instead of an event.** If it's fire-and-forget
cross-boundary state, it's an event; a new request/response endpoint needs a LearnStack
ADR. ADR-0034 retired the "closed at four endpoints" framing — protecting that count is
what caused TLS private keys to be tunnelled through the entitlement payload.
4 changes: 2 additions & 2 deletions .claude/skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Walk all five. Cite `file:line` for every finding; classify Blocker / Major / Mi

- Small single-responsibility methods; descriptive names; no dead code / commented-out blocks; no undated TODO.
- No premature abstraction (three similar lines beat a premature helper); no half-finished implementation.
- The change mirrors the LearnStack pattern it's based on (open `../LearnStack/backend/src/...` and compare) rather than improvising a parallel shape.
- The change mirrors the pattern it's based on. Compare against the Hub's own shipped source first (`backend/src/Core/`, `backend/src/Modules/`); for a LearnStack-only pattern read [`https://github.com/HodeTech/LearnStack/blob/main/backend/src/`](https://github.com/HodeTech/LearnStack/blob/main/backend/src/) — no sibling checkout needed.

### 5. Hub-structural (the delta lens)

Expand All @@ -73,7 +73,7 @@ When delegating to a second agent, compose a self-contained prompt that:

- Sets Hub context + **the five Hub deltas** ([../README.md](../README.md)).
- Names the commit / branch / file list under review (`git log --oneline`, `git show --stat`).
- Points at the LearnStack-side authority (`../LearnStack/docs/...`) the change derives from.
- Points at the LearnStack-side authority it derives from, by absolute URL ([`https://github.com/HodeTech/LearnStack/blob/main/docs/`](https://github.com/HodeTech/LearnStack/blob/main/docs/)).
- Tells the agent to walk all five lenses above.
- Defines the output: verdict + findings as Blocker / Major / Minor / Suggestion with `file:line` + recommendation.
- Insists on reading the surrounding Hub docs (`docs/architecture/`, `docs/modules/`) — don't review in isolation.
Expand Down
11 changes: 9 additions & 2 deletions .claude/skills/commit-and-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,14 @@ Feature branch `feat/phase-02c-packet-N-<slug>`. Push with `-u` only when the us

### Step 5 — Cross-repo coordination (if the packet spans both repos)

Per [CLAUDE.md § Cross-repo coordination](../../../CLAUDE.md): the Hub-side PR opens first (it carries the canonical contract shape); the LearnStack-side PR references the Hub PR's commit hash; both merge in the same session. Adding/changing a contract endpoint requires a new ADR in `../LearnStack/docs/decisions/` first. **Do not** push or merge anything in `../LearnStack` without explicit user permission — another agent may be active there.
Per [CLAUDE.md § Cross-repo coordination](../../../CLAUDE.md), in this order — the sequence is the point, not the list of artefacts:

1. **Merge the LearnStack ADR PR first**, if the packet adds or reshapes a contract endpoint. The ADR lands in the `HodeTech/LearnStack` repo under `docs/decisions/` and is **merged** — not merely opened — before either code PR exists. Code written against an unmerged ADR is code written against a proposal.
2. **Open the Hub code PR.** It carries the canonical contract shape; that is why it goes first.
3. **Open the LearnStack code PR**, referencing the Hub PR's commit hash, written against the shape step 2 fixed.
4. **Merge the two code PRs together**, in the same session. Either-side merge alone leaves the contract dangling.

**Do not** push or merge anything in `../LearnStack` without explicit user permission — including the ADR PR in step 1 — because another agent may be active there.

## Validation

Expand All @@ -76,5 +83,5 @@ Per [CLAUDE.md § Cross-repo coordination](../../../CLAUDE.md): the Hub-side PR

- **Pushing without being asked.** Default is local commit.
- **Amending / force-pushing `main`.** Forbidden — new commits only.
- **A contract change without an ADR.** Adding or reshaping an endpoint on the contract surface needs an ADR in `../LearnStack/docs/decisions/` first — it is a cross-repository agreement (ADR-0034).
- **A contract change without an ADR.** Adding or reshaping an endpoint on the contract surface needs an ADR in the `HodeTech/LearnStack` repo under `docs/decisions/`, merged first — it is a cross-repository agreement ([ADR-0034](https://github.com/HodeTech/LearnStack/blob/main/docs/decisions/0034-hub-contract-surface-invariant.md)).
- **Touching `../LearnStack` branch state from a Hub session.** Coordinate; don't interfere with a parallel agent.
9 changes: 6 additions & 3 deletions .claude/skills/implement-task/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,14 @@ The ten steps are mandatory; skipping any is the bug this skill prevents.

### Step 1 — Scope and alignment

Run [start-task](../start-task/SKILL.md): read the right docs in order (Hub docs under `docs/`, then LearnStack authority under `../LearnStack/docs/`), confirm phase fit against `docs/roadmap/README.md`, walk the [CLAUDE.md hard rules](../../../CLAUDE.md), and pick the specific workflow skill(s) you'll invoke ([add-hub-module](../add-hub-module/SKILL.md), [add-hub-aggregate](../add-hub-aggregate/SKILL.md), [add-mediatr-handler](../add-mediatr-handler/SKILL.md), [wire-cross-cutting-foundation](../wire-cross-cutting-foundation/SKILL.md), …). Output: a one-paragraph problem statement in your own words, the packet it belongs to, the standards that govern it, the skill(s) you'll use.
Run [start-task](../start-task/SKILL.md): read the right docs in order (Hub docs under `docs/`, then LearnStack authority at [`https://github.com/HodeTech/LearnStack/blob/main/docs/`](https://github.com/HodeTech/LearnStack/blob/main/docs/) — read it on GitHub, no sibling checkout needed), confirm phase fit against `docs/roadmap/README.md`, walk the [CLAUDE.md hard rules](../../../CLAUDE.md), and pick the specific workflow skill(s) you'll invoke ([add-hub-module](../add-hub-module/SKILL.md), [add-hub-aggregate](../add-hub-aggregate/SKILL.md), [add-mediatr-handler](../add-mediatr-handler/SKILL.md), [wire-cross-cutting-foundation](../wire-cross-cutting-foundation/SKILL.md), …). Output: a one-paragraph problem statement in your own words, the packet it belongs to, the standards that govern it, the skill(s) you'll use.

### Step 2 — Inspect and understand

Read every file the change touches **before** editing. Trace one hop out (who calls this, who reads this table, what events flow). Read the relevant Hub design spec (`docs/architecture/*.md`, `docs/modules/*.md`) and the LearnStack-side ADR/standard it derives from. If `git log` shows recent edits, read the commit messages for direction. **Mirror, don't invent:** if a pattern exists in `../LearnStack/backend/src/`, open it and reproduce it (adjusting for the Hub deltas), rather than improvising.
Read every file the change touches **before** editing. Trace one hop out (who calls this, who reads this table, what events flow). Read the relevant Hub design spec (`docs/architecture/*.md`, `docs/modules/*.md`) and the LearnStack-side ADR/standard it derives from. If `git log` shows recent edits, read the commit messages for direction. **Mirror, don't invent:** the Hub's own `backend/src/Core/` and `backend/src/Modules/` are
the first place to look — four modules and the SharedKernel are already on `main`. For a
pattern that exists only in LearnStack, read it at
[`https://github.com/HodeTech/LearnStack/blob/main/backend/src/`](https://github.com/HodeTech/LearnStack/blob/main/backend/src/) rather than assuming a local checkout.

### Step 3 — Plan

Expand All @@ -69,7 +72,7 @@ Use [run-tests-locally](../run-tests-locally/SKILL.md): `~/.dotnet/dotnet build

### Step 7 — Update every related document

Walk the list, leave nothing stale: `docs/roadmap/README.md` (packet status); the affected `docs/modules/<name>.md` / `docs/architecture/*.md`; `docs/glossary.md` (new term → [update-glossary](../update-glossary/SKILL.md)); a Hub-internal ADR if a new rule emerged → [write-adr](../write-adr/SKILL.md); the sibling-link audit (CI's `meta` job pattern — `(\.\./)+learnstack/` links are validated locally only).
Walk the list, leave nothing stale: `docs/roadmap/README.md` (packet status); the affected `docs/modules/<name>.md` / `docs/architecture/*.md`; `docs/glossary.md` (new term → [update-glossary](../update-glossary/SKILL.md)); a Hub-internal ADR if a new rule emerged → [write-adr](../write-adr/SKILL.md); the link audit (CI's `meta` job **fails** on a sibling-relative `../LearnStack/...` Markdown link; use the absolute GitHub URL).

### Step 8 — Commit

Expand Down
9 changes: 7 additions & 2 deletions .claude/skills/local-dev-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Get the Hub stack running. Hub's compose is deliberately minimal — it runs onl
### Step 1 — Bootstrap (one-time)

```bash
cd ~/Documents/Projects/learnstack-hub
cd ~/Documents/Projects/LearnStack-Hub
make install # restores backend NuGet + frontend pnpm + activates .githooks/pre-commit
```

Expand All @@ -63,7 +63,12 @@ Brings up the Hub-only services: Dapr placement (50006), Dapr sidecar (3501/5000
### Step 4 — Hub API

```bash
cd ../LearnStack-Hub/backend
# The Api host does not read .env — export it the way scripts/seed.sh does,
# or the connection string falls back to defaults and Postgres is unreachable.
cd ../LearnStack-Hub
set -a; . ./.env; set +a
export POSTGRES_HOST=localhost
cd backend
~/.dotnet/dotnet run --project src/Core/LearnStack.Hub.Api # binds 0.0.0.0:5181
```

Expand Down
12 changes: 10 additions & 2 deletions .claude/skills/standards-check/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ Walk these against the diff. Each item is pass/fail; a fail blocks merge until f

### Contract surface + boundary ([CLAUDE.md hard rules](../../../CLAUDE.md))

- [ ] No fifth Hub HTTPS endpoint without an ADR in `../LearnStack/docs/decisions/`.
- [ ] The Hub stores no tenant content (`Hub_NeverStores_TenantData`), and every
LearnStack↔Hub crossing goes through `IEntitlementProvider` / `IUsageReporter` /
`IHubTenantSync` — nothing else holds a Hub client. Those are
[ADR-0034](https://github.com/HodeTech/LearnStack/blob/main/docs/decisions/0034-hub-contract-surface-invariant.md)'s two invariants;
the surface is **not** governed by an endpoint count. Any endpoint added to the set
needs a merged LearnStack ADR first.
- [ ] `/api/internal/*` not internet-exposed (when those endpoints land).
- [ ] No Kubernetes-credential / K8s-state writes to LearnStack's cluster.
- [ ] `learnstack-hub` realm boundary respected.
Expand All @@ -71,7 +76,10 @@ Walk these against the diff. Each item is pass/fail; a fail blocks merge until f
### Docs + corpus hygiene

- [ ] Adjacent docs updated (module deep dive, glossary, roadmap status, ADR cross-link).
- [ ] Sibling-relative links (`(\.\./)+learnstack/...`) resolve locally.
- [ ] **No** sibling-relative Markdown link to LearnStack (`../LearnStack/...`) anywhere —
cross-repo references are absolute `https://github.com/HodeTech/LearnStack/blob/main/...` URLs, and CI's `meta` job **rejects** the
relative form. `../LearnStack` stays correct for shell paths only. Every other relative
link resolves from its own file's directory.
- [ ] English docs; Mermaid diagrams readable as text.

## Output
Expand Down
Loading
Loading