Skip to content

docs: own the Hub roadmap here, and reconcile the LearnStack boundary - #2

Merged
cemililik merged 14 commits into
mainfrom
docs/hub-roadmap
Aug 10, 2026
Merged

docs: own the Hub roadmap here, and reconcile the LearnStack boundary#2
cemililik merged 14 commits into
mainfrom
docs/hub-roadmap

Conversation

@cemililik

@cemililik cemililik commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Updated 2026-08-09 — P02c-1 merged, and this PR now builds on it.
PR #1 landed while this was open. Before merging it, its 221 files were reviewed
against the three decisions the restructure moved, and it implements none of them:
no hosted endpoint and no LearnStackApiClient (both P02c-2), an AuditLogBehavior
that is a shell with no IAuditStore and no audit_log table, and an
entitlement-v1.schema.json that already carries grace_until and generation
with the wire names ADR-0034 fixed — and no certificate, key, host or domain field.

So the freeze was reframed rather than dropped: it is on the track's forward
motion, not on the merged artefact.
P02c-1 is ✅ Shipped; P02c-2 onward is ⏸
Frozen on the ADR-0035 trigger, which is where the contract surface ADR-0034
redrew actually gets built. Holding P02c-1 would have bought nothing and cost the
SharedKernel reconciliation against LearnStack Packet 3b, which grows with every
packet on top — that reconciliation is now tracked in
docs/roadmap/p02c-1-hub-domain-core.md alongside the ADR-0033 audit seam.

Four conflicts resolved, plus one defect that merged silently: ci.yml's header
said backend-integration was if: false until P02c-2 while the job below it ran
ungated. P02c-1 shipped the first Testcontainers tests, so activating it there is
correct and the five carriers saying otherwise are fixed. Three source comments
claiming Hub drops "the two tenant-isolation steps" from "core's 8-step pipeline"
are corrected too — Hub drops exactly one behavior.


The Hub half of a two-repository roadmap restructure. Opens first per
CLAUDE.md § Cross-repo coordination — this side carries the
canonical contract shape, and the LearnStack PR references it.

Paired PR: HodeTech/LearnStack#9 — both merge in the same session.
Either side alone leaves the contract dangling.

Documentation only. No source, no schema, no CI behaviour change except
one deliberate link-audit tightening (below).


What this is

Two things, in sequence. First the Hub roadmap moved into this repository,
which owns it. Then a four-report audit of the corpus produced 151 findings
across both repos, and this PR closes the Hub's share of them.

The audit's own recurring failure mode is worth stating up front, because it
shaped how the fixes were applied: the corpus fixed the documents it touched
and left the untouched carriers of the same fact stale.
Every fix here was
applied by sweeping for every carrier of a claim, not the one the finding
happened to cite. Three findings under-counted their own carriers by half or
more.

The Hub-side changes that matter

The CI link audit was broken and nobody could see it. The workflow skips
the file-existence check for cross-repo links, because CI cannot reach a
sibling checkout — but the skip pattern matched learnstack in lowercase
only, and bash =~ is case-sensitive with no nocasematch set. A prior
commit had recapitalised all 215 cross-repo links to ../LearnStack/..., so
every one of them fell through to a check that could not pass. Fixed, then
made moot: the links are now absolute URLs and the exemption is removed
entirely
, so a relative cross-repo link fails the build instead of reaching
a reader.

222 cross-repo links became absolute URLs. LearnStack's Documentation
Standards § Layout says cross-repo references use absolute URLs; this
repository used sibling-relative paths at four different ../ depths. Every
one 404s on github.com, and moving a document one directory deeper silently
broke all of its cross-repo links. Link targets only — cd ../LearnStack and
filesystem references in prose keep the relative path, because those are paths,
not links. An earlier, broader sweep rewrote both and produced
cd https://github.com/...; it was discarded rather than patched.

The contract pointer file was pointing at the wrong authority.
docs/architecture/contract-with-learnstack.md is pointer-only and is what a
Hub-side agent reads instead of the LearnStack corpus — so a stale pointer
here is worse than a stale statement anywhere else. It named ADR-0019 as the
source for "the endpoint set + auth chain", one row for two topics that
ADR-0034 splits. It also said "the two LearnStack → Hub endpoints" and "the two
Hub → LearnStack endpoints" — a count that was wrong under ADR-0019 (six paths)
and wrong under ADR-0034 (nine). Counts removed rather than corrected: a
pointer file that carries a number acquires the maintenance obligation it
exists to avoid.

P02c-5 was blocked on the wrong phase. The blocking table pointed it at
LearnStack Phase 11 for the host-mapping handler; LearnStack's own
27-custom-domain-tls.md § 11 puts that handler in Phase 02c and only the ACME
automation in Phase 11. Split into two rows, and the Phase 11 row carries an
explicit "P02c-5 does not wait on it" — without that clause the split
silently re-gates the packet by a different route.

P02c-6 gained the phone-home client certificate. ADR-0034 replaces the
per-instance API key on the LearnStack → Hub direction with the same
mTLS + JWT + HMAC chain the other direction carries. That leaves a
SelfHostedOnline instance unable to phone home at all unless something hands
it a certificate the Hub's CA validates — and P02c-6 is the only packet that
hands a customer-run instance anything. It rides with the .lic file: same
license_id, same re-issue command, same revocation, so a revoked instance
loses the transport before it loses the entitlement.

Three packets cross repositories, not one. P02c-3 called itself "the only
packet that changes both repositories" while P02c-5 carries the LearnStack-side
host-mapping handler and P02c-6 the SignedLicenseKeyEntitlementProvider
skeleton. Both missing blocking-table rows added, including P02c-3's dependency
on LearnStack Phase 02b — the outbox processor and inbox guard, without
which IUsageReporter cannot dispatch through the outbox.

Hub production deployment had no owner. The P02c-7 exit gate pointed at
LearnStack Phase 11, which scopes itself to LearnStack and does not cover the
Hub's own database, realm or certificate chain. A "Hub Operations" track joins
the post-MVP table — deliberately unlinked, because no plan document exists and
a dead link is worse than an unlinked row.

The pipeline arithmetic was off by one. cross-cutting-foundation.md said
"LearnStack runs an 8-step pipeline … Hub runs 6-step. The two steps that drop
out are…" and then listed one. LearnStack's eight counts the Handler; Hub's six
does not. Both sides now count behaviors — seven and six — and the document
says which convention it uses.

Plus the glossary corrections (generation's acceptance inequality was
inverted; LicenseKey named a third spelling of the timestamps P02c-6 exists
to reconcile), the plan editor's owner (Hub Billing, not P02c-4), soft-limit
alert provenance (produced by LearnStack, ingested by P02c-2), and Status
banners on P02c-4 through P02c-7.

Verification

A nine-check script ran after every wave against both repositories and returned
zero: relative links resolved case-sensitively (macOS APFS hides the casing bug
that breaks Linux CI), frozen delivery records byte-identical, phase-doc section
conformance, mermaid well-formedness, Markdown table column counts, no committed
file citing the gitignored docs/analysis/, retired claims not resurfacing,
sibling-path casing, and balanced code fences.

Three of those checks were added during execution, each because something got
through: a dropped closing fence passed all eight existing checks (the table
check reads an unclosed fence as "still in code" and stops inspecting); a
retired claim's carriers were under-counted for the fourth time; and the
frozen-record check compared line numbers against a hard-coded boundary that a
structural move erased — it now extracts the record by content and requires
byte-identity.

Review notes

  • Not touched on purpose: cemililik/leakwatch (Go module path — its
    canonical form is fixed by the module's own go.mod, so rewriting the install
    command can break go install rather than redirect it), the Homebrew tap, and
    @cemililik in CODEOWNERS, which names a person rather than a repository.
  • One design question stays open by choice: whether
    ICacheService.RemoveByPrefixAsync leaves the port or becomes a
    generation-key pattern. ADR-0035 permits both, every downstream document
    survives either, and Phase 02a Packet 5 owns it.

🤖 Generated with Claude Code

Summary by Sourcery

Own the Hub roadmap and contract documentation in this repository, align cross-repo contracts and invariants with updated LearnStack ADRs, and tighten link/audit and sibling-layout rules while clarifying the Hub’s structural deltas from LearnStack core.

Enhancements:

  • Clarify and correct the Hub↔LearnStack contract surface narrative to be governed by two invariants and updated endpoint sets instead of a fixed endpoint count, aligning with ADR-0034.
  • Document the intra-Hub dependency graph, status ledger, and post-MVP tracks to make packet ownership, cross-repo coordination, and triggers explicit.
  • Record that P02c-1 is implemented but frozen on a branch, and define unfreeze conditions tied to ADR-0033/0034/0035 and demand-gated infrastructure.
  • Refine repository layout, module topology, SharedKernel mirroring, architecture tests, and local dev setup docs to emphasise Hub’s lack of RLS, tenant content storage, and direct LearnStack imports.

CI:

  • Tighten the Markdown link audit in CI by removing the exemption for sibling-relative LearnStack links so only absolute external URLs are allowed, causing relative cross-repo links to fail the build.

Documentation:

  • Rewrite docs/roadmap/README.md and add per-packet and post-MVP track documents so this repo owns the Hub roadmap and cross-repo blocking tables instead of mirroring LearnStack’s plan.
  • Expand and clarify architecture, module, glossary, CLAUDE, AGENTS, CONTRIBUTING, infra, operations, and skills documentation to reflect Hub-specific conventions, six-step pipeline, OperatorId, shared services, and demand-gated infrastructure.
  • Update all Hub docs to reference LearnStack by absolute GitHub URLs with corrected repository ownership, ADR list, standards corpus, and glossary, avoiding duplicated cross-cutting content.
  • Introduce detailed packet docs for P02c-0..7, Hub Billing, Hub Marketplace, and Hub Operations, including goals, scope, deliverables, completion criteria, risks, and phase-exit decisions.

Summary by CodeRabbit

  • Documentation

    • Standardized LearnStack and LearnStack-Hub paths and canonical links.
    • Updated architecture, contract, glossary, roadmap, contribution, and development guidance.
    • Added roadmaps for billing, marketplace, operator portal, integrations, custom domains, licensing, and release gates.
    • Clarified Hub–LearnStack boundaries, authentication, tenancy, certificates, and pipeline behavior.
  • CI and Tooling

    • Improved link auditing to validate sibling-repository references and report broken links.
    • Updated repository status, workflow guidance, and pull request requirements.

cemililik and others added 10 commits August 8, 2026 13:05
The Hub plan lived in LearnStack's phase-02c-hub-foundation.md, and this
repository carried a 43-line status mirror that had been stale for four
commits. It reflected neither the 18-skill catalogue nor the five P02c-1
design specs that shipped after it.

docs/roadmap/ is now the authoritative Hub plan: an index with an intra-Hub
dependency map and a cross-repo blocking table in both directions, plus a
document per packet carrying Goal, Scope, Deliverables, Completion Criteria,
Risks and Phase Exit Decision. hub-billing.md and hub-marketplace.md migrate
from LearnStack's phase-09b and phase-12, which become pointers.

The P02c-N identifiers are unchanged. They appear in branch names, commit
subjects, skill bodies and design specs across both repositories, and
renumbering would orphan every one of them.

Contract and durability changes tracked from LearnStack:

- ADR-0034 replaces the "closed at four endpoints" rule with two invariants:
  the Hub stores no tenant content, and every crossing goes through a named
  adapter. P02c-5 accordingly moves certificate material out of the
  entitlement payload -- host mappings carry the tenant tuple only, and cert
  material moves by secret-store replication, referenced by path.
- P02c-6 adds license_id to the licence payload; revocation depends on it and
  the documented claim set omitted it.
- P02c-1 is recorded as implemented-on-branch and frozen, with the condition
  that unfreezes it: a tenant must be billed or plan-gated (the ADR-0035
  trigger for the Hub entitlement adapter).

Stale facts corrected:

- Every sibling path now uses the real directory names, ../LearnStack and
  ../LearnStack-Hub. Roughly 130 references assumed lowercase and resolved
  only because macOS is case-insensitive; they would break on Linux CI, which
  cannot catch them because the link audit deliberately skips sibling links.
  Three files also ascended one level too far.
- docs/README.md and AGENTS.md claimed this repository maintains no skill
  catalogue. It maintains 18 skills.
- Program.cs described an 8-step MediatR pipeline; Hub's is 6-step, with no
  TenantContextBehavior, because it stores no tenant content.
- repository-layout.md listed two architecture docs where there are five, and
  marked docs/modules/ as future when four module specs had shipped.
- The Co-Authored-By trailer named a stale model version.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ation

An independent pass found no blockers and three gaps on the Hub side, each a
place where a rule LearnStack's ADR-0034 replaced survived in a file the
migration touched only for path casing.

- The four-endpoint claim persisted in `docs/glossary.md`, in
  `contract-with-learnstack.md` -- the file whose entire job is pointing at
  the authoritative contract spec, and which never mentioned ADR-0034 -- and
  in `code-review`, `commit-and-pr` and `start-task`, the three skills that
  gate every change. Also in the PR template and `.env.example`. Two
  repositories carried one glossary each, stating opposite facts about the
  same boundary. All now state the two invariants: the Hub stores no tenant
  content, and every crossing goes through a named adapter.
- The Co-Authored-By trailer still named a stale model version in
  `CONTRIBUTING.md`, `commit-and-pr` and the P02c-1 implementation prompt.
- `docs/modules/tenant-lifecycle.md` deferred the
  hard-delete-with-confirmation flow to "a later packet" without naming it,
  against a CLAUDE.md hard rule. It is P02c-4, which builds the operator
  surface that confirms the deletion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…doc corrections

Paired with LearnStack's harvest-round commit; same wave, one commit per
repository.

The blocker: `.github/workflows/ci.yml`'s link audit skips the existence
check for cross-repo links because CI cannot reach the sibling checkout,
but the skip pattern matched `learnstack` in lowercase only. Bash `=~` is
case-sensitive and this workflow sets no `nocasematch`. Commits eb85c2b
and f105d07 recapitalised all 215 cross-repo links to `../LearnStack/...`,
so every one of them fell through to a check that cannot pass. The pattern
now matches both capitalisations explicitly rather than depending on a
shell option set elsewhere; the trailing `(/|$)` still keeps a
hypothetical `../LearnStack-Hub/...` self-reference out of the skip.

Also:

- P02c-1's out-of-scope list said "the four HTTPS contract endpoints".
  ADR-0034 replaced the four-endpoint claim with two invariants over nine
  endpoints; the sentence needed no count at all.
- p02c-5's event table had the custom-domain events inserting and
  deleting `platform_host_to_tenant` rows directly. The mapping travels
  over PUT /api/internal/tenants/{id}/host-mappings; the events invalidate
  the resolver cache.
- p02c-6's licence-expiry ladder read as though it should be reconciled
  with ADR-0034's Hub-outage ladder. They are deliberately different — an
  outage is a control-plane failure the tenant did not cause, an expired
  licence is the licence working — and the paragraph now says so, so a
  later sweep does not "fix" it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e bundle

Paired with LearnStack's wave-4 commit; same wave, one commit per
repository.

ADR-0034 § One auth chain, both directions replaces the per-instance API
key on the LearnStack -> Hub direction with the same mTLS + RS256 JWT +
HMAC chain the other direction already carries. That leaves a
SelfHostedOnline instance unable to phone home at all unless something
hands it a client certificate the Hub's CA will validate, and P02c-6 is
the only packet that hands a customer-run instance anything.

The certificate rides with the .lic file: same license_id, same re-issue
command, same expiry cadence, same revocation. One artefact to issue, one
to rotate, one to revoke — and revoking a licence takes the transport
away before it takes the entitlement away. SelfHostedAirGapped makes no
outbound call and is issued none.

The completion criterion asserts the negative case at the layer that
should catch it: a phone-home with no certificate, an expired one, or one
belonging to a revoked licence is refused at the TLS handshake, before
any handler runs and before the JWT or the body signature is examined.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…counts

Paired with LearnStack's wave-6 commit; same wave, one commit per
repository.

This file is pointer-only and its pointers had gone stale in a way that
matters more here than anywhere else: it is what a Hub-side agent reads
instead of the LearnStack corpus. It named ADR-0019 as the source for
"the endpoint set + auth chain" — one row for two topics that ADR-0034
now splits, replacing the endpoint framing entirely and extending the
auth chain to the LearnStack -> Hub direction. The invariants row pointed
at Standards 20 and ADR-0019 rather than at the ADR that introduced them.

The packet table said "the two LearnStack -> Hub endpoints" and "the two
Hub -> LearnStack endpoints" — a count that was wrong under ADR-0019 (six
paths) and is wrong under ADR-0034 (nine). Counts removed rather than
corrected; a pointer file that carries a number acquires a maintenance
obligation it exists to avoid.

P02c-5's row still described the custom-domain events as the channel that
writes `platform_host_to_tenant`. They are the cache-invalidation signal;
the mapping travels over `PUT /api/internal/tenants/{id}/host-mappings`,
and certificate material moves by secret-store replication rather than
through the entitlement payload — ADR-0034 superseding ADR-0022
Amendment 1 step 3.

The ADR-0022 row now says which part of Amendment 1 survives, and the
architecture-test enumeration gains
`Hub_Client_Referenced_Only_By_Named_Adapters` with the counts corrected
to seven.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…e 11

Paired with LearnStack's wave-7 commit; same wave, one commit per
repository.

The blocking table had one row pointing P02c-5 at LearnStack Phase 11 for
"the LearnStack-side host-mapping handler". LearnStack's
27-custom-domain-tls § 11 puts that handler and the
platform_host_to_tenant mirroring in Phase 02c, and only the ACME
automation and the edge certificate-installation path in Phase 11 — host
resolution is a one-way door, the automation that populates the mapping
is additive.

Split into two rows so the distinction survives: the Phase 02c row is the
paired half of this packet, merged in the same session; the Phase 11 row
is the edge half and carries an explicit "P02c-5 does not wait on it",
because host resolution works from the platform_host_to_tenant row alone.
Without that clause the split silently re-gates P02c-5 on Phase 11 by a
different route.

The "LearnStack waits on Hub" table's matching row said the mirror
handler was Phase 11; corrected to Phase 02c.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Wave 8, Hub half.

LearnStack's Documentation Standards § Layout says plainly that
"cross-repo references use absolute URLs". This repository had 222
Markdown links written as sibling-relative paths — `../LearnStack/...`,
`../../LearnStack/...`, `../../../LearnStack/...` depending on the source
file's depth — across 37 files. Every one of them 404s on github.com,
where a relative link cannot cross a repository boundary, and every one
depends on the reader having a sibling checkout that is present and
identically capitalised. The depth-dependence is its own hazard: moving a
document one directory deeper silently breaks all its cross-repo links.

The sweep rewrites link targets only. Shell commands (`cd ../LearnStack`)
and filesystem references in prose and code spans keep the relative path,
because those are paths, not links, and the sibling layout is still real.
An earlier, broader pass rewrote both and produced `cd
https://github.com/...`; it was discarded rather than patched.

With the links absolute, the CI link audit's cross-repo exemption is
removed rather than kept. It existed because CI cannot reach a sibling
checkout — but the external-URL skip already covers absolute URLs, and a
relative cross-repo link is now a defect the audit should catch. It fails
the build instead of reaching a reader.

The four documents that taught the old convention — README, docs/README,
CLAUDE.md, AGENTS.md — now separate the two rules that were conflated:
links are absolute URLs; shell and filesystem paths stay relative and
case-sensitive.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…r of Hub operations

Paired with LearnStack's waves 9-10 commit.

P02c-3 called itself "the only packet in the P02c series that changes
both repositories". P02c-5 carries the LearnStack-side host-mapping
handler and P02c-6 the SignedLicenseKeyEntitlementProvider skeleton —
three packets, not one — and the "unblocked by LearnStack" sentence
listed P02c-6 among them. Both corrected, and the two missing rows added
to the blocking table: P02c-3 on LearnStack Phase 02b (the outbox
processor, the inbox guard, handler-scope context restoration — without
which IUsageReporter cannot dispatch through the outbox and the
entitlement invalidation consumer has no guard), and P02c-6 on P02a-9's
IEntitlementProvider socket. P02c-3 is the only packet gated on the
LearnStack spine reaching Phase 02b rather than merely Phase 02a, and the
README now says so.

Hub production deployment pointed at LearnStack Phase 11, which scopes
itself to LearnStack and does not cover the Hub's own database, realm or
certificate chain — so the P02c-7 exit gate named a dependency nobody
owns. A "Hub Operations" track joins the post-MVP table, deliberately
unlinked because no plan document exists yet and a dead link is worse
than an unlinked row.

P02c-5 gains the inbound custom-domain submission handler as a
deliverable — the hop LearnStack's Admin Studio proxies because the Hub
rejects a `learnstack` realm token — and its ADR-0022 Amendment 2
citation now says which half of that amendment survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Paired with LearnStack's waves 11-12 commit.

The pipeline arithmetic. cross-cutting-foundation said "LearnStack runs
an 8-step pipeline ... Hub runs a 6-step pipeline. The two LearnStack
steps that drop out are..." and then listed one. LearnStack's eight-step
list counts the Handler; Hub's six does not. Both sides now count
behaviors — seven and six — and the document says which convention it
uses, so the delta is one behavior and the arithmetic works. p02c-1 and
the two derived mentions follow.

The glossary was restating rules it does not own. `generation` carried
`cached.generation >= received.generation` — the inequality inverted, and
a third independent statement of a LearnStack-side acceptance predicate;
it now defines the counter and points at the one place the acceptance
rule lives. `LicenseKey` named `valid_from` / `valid_until` and "public
key hash" where P02c-6's column table says `issued_at` / `expires_at`,
`kid` and `entitlement_generation` — a third spelling of the timestamps
P02c-6 exists to reconcile. `Entitlement` now says plainly that the
Hub-side aggregate is the only writer anywhere and that what crosses the
wire is the distinct *Entitlement Projection*.

The plan editor was assigned to P02c-4 in the module doc and to Hub
Billing in the roadmap; Hub Billing wins, so a plan's feature payload and
its price are edited on one form. Soft-limit alerts were described as
"produced since P02c-2" — they are produced by LearnStack at the gated
call site and merely ingested by P02c-2; this track adds the Hub half.
The marketplace row advertised revenue share, which its own plan puts out
of scope. P02c-3's reconciliation named IHubTenantSync re-pushing a
tenant, but IHubTenantSync is the LearnStack-side handler; the Hub
re-issues POST /api/internal/tenants and the handler is idempotent on the
Hub-minted id.

Polish: Status banners on P02c-4 through P02c-7, which opened straight
into Goal while P02c-1 through P02c-3 each carry one; the operator-portal
naming ledger entry closed with its three real residues named, and the
Keycloak client id explicitly excluded from the rename; and one lowercase
sibling path in CONTRIBUTING.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ually has

Paired with LearnStack's commit of the same name.

This repository carried 248 references to `cemililik/LearnStack` — 222 of
them the absolute cross-repo links introduced in the previous commit, the
rest in prose and packet documents. Both repositories now live in the
HodeTech organisation; `git remote get-url origin` says so.

The old URLs still resolve, via GitHub's 301 rename redirect. That is the
reason to fix them now rather than later: the redirect holds only while
nothing occupies the old path, and `cemililik` is a live personal
account. If anything is ever created there, these links resolve to a
different repository instead of failing.

Left untouched on purpose: `cemililik/leakwatch` in the pre-commit hook
and CI (a Go module path, whose canonical form is set by the module's own
go.mod — rewriting it can break `go install` rather than redirect it),
the Homebrew tap, and `@cemililik` in CODEOWNERS, which names a person.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @cemililik, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 607b2312-25c0-4df9-9f65-0250a0f104d9

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3041e and 6faf7ab.

📒 Files selected for processing (1)
  • docs/roadmap/p02c-1-hub-domain-core.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/roadmap/p02c-1-hub-domain-core.md

📝 Walkthrough

Walkthrough

The changes align Hub guidance, documentation, architecture references, repository paths, CI link checks, contract invariants, pipeline descriptions, and roadmap packets with the current P02c-1 state and planned cross-repository work.

Changes

Hub documentation alignment

Layer / File(s) Summary
Skill and link guidance
.claude/skills/*
Updated LearnStack paths, authoritative URLs, contract rules, workflow instructions, and commit guidance.
Repository governance
AGENTS.md, CLAUDE.md, CONTRIBUTING.md, .env.example, .github/pull_request_template.md
Updated repository ownership, contribution rules, contract invariants, milestone status, path conventions, and HMAC documentation.
Architecture and reference documentation
README.md, docs/architecture/*, docs/modules/*, docs/glossary.md, docs/operations/*, backend/src/*, backend/tests/*
Documented current Hub boundaries, six MediatR behaviors, entitlement semantics, repository layout, authoritative references, and architecture-test descriptions.
Roadmap governance and integration packets
docs/roadmap/README.md, docs/roadmap/p02c-0-*, docs/roadmap/p02c-1-*, docs/roadmap/p02c-2-*, docs/roadmap/p02c-3-*
Added roadmap governance, packet states, integration contracts, authentication, synchronization, dependencies, and exit criteria.
Post-MVP roadmap packets
docs/roadmap/hub-*.md, docs/roadmap/p02c-4-* through p02c-7-*
Added billing, marketplace, operator portal, custom-domain, license-key, and end-to-end exit-gate specifications.
Infrastructure and CI validation
infra/compose/*, infra/keycloak/*, .github/workflows/ci.yml
Updated local repository paths, activated the backend integration milestone, and validated sibling-relative LearnStack links.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: Hub roadmap ownership and LearnStack boundary reconciliation.
Description check ✅ Passed The detailed description covers the roadmap, phase, ADR, cross-repository, testing, and risk information required by the template.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/hub-roadmap

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR migrates ownership of the Hub roadmap into the Hub repo, tightens and corrects cross-repo contract documentation versus updated ADRs, converts all cross-repo documentation links to absolute GitHub URLs, and fixes the CI markdown link audit so relative cross-repo links are treated as defects rather than exempted, alongside several roadmap, glossary, and skills-catalogue reconciliations.

Sequence diagram for updated CI markdown link audit behaviour

sequenceDiagram
    participant CI as GitHub_Actions_CI
    participant LinkAudit as MarkdownLinkAudit

    CI->>LinkAudit: Run link_audit_script
    loop For each link
        LinkAudit->>LinkAudit: Check_scheme(link)
        alt External URL (http/https/mailto...)
            LinkAudit->>LinkAudit: Skip (no filesystem check)
        else Relative path (incl. ../LearnStack/...)
            LinkAudit->>LinkAudit: Normalize link_path
            LinkAudit->>LinkAudit: Test file existence
            alt File missing
                LinkAudit->>CI: Report failure (relative link defect)
            else File exists
                LinkAudit->>LinkAudit: Continue
            end
        end
    end
Loading

File-Level Changes

Change Details Files
Hub roadmap is now owned and fully documented in the Hub repository, replacing the prior status-mirror file.
  • Replaced docs/roadmap/README.md status mirror with a full Hub-owned roadmap including packet docs P02c-0 through P02c-7 and post-MVP tracks.
  • Documented intra-Hub dependencies and cross-repo blocking tables (Hub waits on LearnStack and vice versa).
  • Added detailed status ledger and clarified P02c-1 branch frozen state and unfreeze conditions.
  • Introduced new roadmap documents for individual packets, Hub Billing, and Hub Marketplace with clear goals, scope, deliverables, and exit criteria.
docs/roadmap/README.md
docs/roadmap/p02c-0-repository-bootstrap.md
docs/roadmap/p02c-1-hub-domain-core.md
docs/roadmap/p02c-2-internal-api-and-contract.md
docs/roadmap/p02c-3-learnstack-integration.md
docs/roadmap/p02c-4-operator-portal.md
docs/roadmap/p02c-5-custom-domain-lifecycle.md
docs/roadmap/p02c-6-license-key.md
docs/roadmap/p02c-7-exit-gate.md
docs/roadmap/hub-billing.md
docs/roadmap/hub-marketplace.md
Cross-repo documentation links and sibling layout references are corrected to use absolute GitHub URLs and the canonical LearnStack/LearnStack-Hub capitalization.
  • Updated README, docs/README.md, CLAUDE.md, CONTRIBUTING.md, infra docs, skills, and module docs to use absolute URLs to LearnStack’s docs and standards instead of relative ../learnstack paths.
  • Standardized shell and filesystem references to sibling directories as ../LearnStack and ../LearnStack-Hub and documented the case-sensitivity implications for Linux CI.
  • Adjusted skills, prompts, and local-dev instructions to refer to the sibling repo as ../LearnStack rather than ../learnstack.
README.md
docs/README.md
CLAUDE.md
CONTRIBUTING.md
infra/compose/README.md
infra/keycloak/README.md
.claude/skills/README.md
.claude/skills/local-dev-setup/SKILL.md
.claude/skills/implement-task/SKILL.md
.claude/skills/start-task/SKILL.md
.claude/skills/commit-and-pr/SKILL.md
.claude/skills/write-adr/SKILL.md
.claude/skills/update-glossary/SKILL.md
.claude/skills/wire-cross-cutting-foundation/SKILL.md
.claude/skills/add-feature-key/SKILL.md
.claude/skills/standards-check/SKILL.md
.claude/skills/code-review/SKILL.md
.claude/skills/add-ef-migration/SKILL.md
.claude/skills/add-hub-module/SKILL.md
.claude/skills/add-integration-event/SKILL.md
.claude/skills/add-architecture-test/SKILL.md
CI markdown link audit is fixed and hardened so cross-repo links must be absolute URLs and relative cross-repo paths fail the build.
  • Removed the bash exemption that skipped any sibling-relative ../learnstack links in the markdown link audit.
  • Documented that Hub docs now link into LearnStack core only by absolute GitHub URLs and that relative cross-repo links are considered defects.
  • Updated comments in the workflow to match the new link policy and the LearnStack capitalization.
.github/workflows/ci.yml
Contract and architecture documentation is reconciled with ADR-0034, ADR-0035, ADR-0033 and updated to focus on invariants rather than endpoint counts or outdated flows.
  • Updated contract-with-learnstack.md to reference ADR-0034 for the endpoint set and invariants and removed the 'closed four-endpoint' language.
  • Aligned CLAUDE.md hard rules and contract surface description with ADR-0034, including the two invariants, TLS key handling and adapter-only crossings.
  • Refined cross-cutting-foundation.md, module-topology.md, entitlement-projection.md and glossary entries to match current ADRs and LearnStack architecture documents.
  • Added and clarified architecture test expectations and their canonical identifiers per Standards 21.
docs/architecture/contract-with-learnstack.md
CLAUDE.md
docs/architecture/cross-cutting-foundation.md
docs/architecture/module-topology.md
docs/architecture/entitlement-projection.md
docs/architecture/README.md
docs/decisions/README.md
docs/glossary.md
backend/tests/LearnStack.Hub.Tests.Architecture/HubBoundaryTests.cs
backend/tests/LearnStack.Hub.Tests.Architecture/RepositoryLayoutTests.cs
Repository and documentation layout descriptions are updated to reflect current on-main structure, tests, modules, docs, and skills catalogue.
  • Refreshed docs/architecture/repository-layout.md to describe actual on-main backend, tests, frontend, infra, docs, scripts, and skills directories, including architecture tests and module specs.
  • Added 'Documentation on disk' section detailing what lives in docs/architecture, docs/modules, docs/roadmap, docs/decisions, docs/operations, and glossary.
  • Clarified planned module topology table to note which modules are specified but not yet on main and updated comparison with LearnStack core.
  • Documented that the Hub maintains its own .claude/skills catalogue and how it differs from LearnStack’s skills.
docs/architecture/repository-layout.md
docs/modules/README.md
.claude/skills/README.md
AGENTS.md
Roadmap packet docs are extended with detailed goals, scope, deliverables, risks, and exit criteria for P02c-4, P02c-5, P02c-6, P02c-7, and post-MVP tracks.
  • Authored full spec for P02c-4 Operator Portal MVP, including authentication model, Operators and Audit modules, screens, and risks.
  • Authored full spec for P02c-5 Custom Domain Lifecycle and Compliance, including ACME adapter, events, secret-store replication, and host-mapping semantics.
  • Authored full spec for P02c-6 License Key, including LicenseKey aggregate, .lic payload schema, revocation model, and LearnStack-side skeleton.
  • Authored full spec for P02c-7 End-to-End Exit Gate, defining seven cross-repo gates and end-to-end verification.
  • Defined Hub Billing and Hub Marketplace tracks moved from LearnStack’s roadmap into the Hub repo, with triggers and non-goals.
docs/roadmap/p02c-4-operator-portal.md
docs/roadmap/p02c-5-custom-domain-lifecycle.md
docs/roadmap/p02c-6-license-key.md
docs/roadmap/p02c-7-exit-gate.md
docs/roadmap/hub-billing.md
docs/roadmap/hub-marketplace.md
Various guidance and skills documents are updated to reflect the current AI co-author trailer, cross-repo coordination rules, and LearnStack standards references.
  • Updated the AI co-author trailer from Claude Opus 4.7 to Claude Opus 5 across AGENTS.md, CONTRIBUTING.md, skills, and commit guidance.
  • Clarified cross-repo coordination protocol for packets that change both repositories, including ADR requirements before contract changes.
  • Aligned standards references in skills and docs to use absolute URLs to LearnStack’s standards corpus.
  • Adjusted some module docs (plans, subscriptions, tenant-lifecycle, entitlements) to reference current LearnStack ADRs and architecture sections and correct minor narrative inconsistencies.
AGENTS.md
CONTRIBUTING.md
.claude/skills/commit-and-pr/SKILL.md
.claude/skills/standards-check/SKILL.md
.claude/skills/add-feature-key/SKILL.md
docs/modules/plans.md
docs/modules/subscriptions.md
docs/modules/tenant-lifecycle.md
docs/modules/entitlements.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (13)
docs/roadmap/p02c-4-operator-portal.md-122-131 (1)

122-131: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use one repository-relative path for the operator portal.

The section names frontend/apps/operator-portal but later uses apps/operator-portal for the same application. From the repository root, these are different paths. Use frontend/apps/operator-portal for repository-root references, or explicitly label apps/operator-portal as frontend-workspace-relative.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/p02c-4-operator-portal.md` around lines 122 - 131, Standardize
all repository-root references to the operator portal on
frontend/apps/operator-portal, including the later apps/operator-portal mention
in this section. If retaining apps/operator-portal, explicitly identify it as
frontend-workspace-relative.
README.md-30-34 (1)

30-34: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language to the directory-layout fence.

The Markdown linter reports MD040 because this fenced block has no language. Use text for the directory tree.

Proposed fix
-```
+```text
 <parent-dir>/
 ├── LearnStack/        (https://github.com/HodeTech/LearnStack)
 └── LearnStack-Hub/    (this repo)
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @README.md around lines 30 - 34, Add the text language identifier to the
directory-layout fenced block in README.md by changing its opening fence to use

Source: Linters/SAST tools

.github/workflows/ci.yml-174-178 (1)

174-178: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Update the stale link-audit comment.

Lines 174-178 still say that CI skips ../LearnStack/ paths. The audit now deliberately rejects those paths in Lines 201-208. This comment can mislead maintainers into adding invalid relative documentation links.

As per coding guidelines, sibling-relative paths are reserved for filesystem and shell usage; documentation links must use absolute GitHub URLs.

Proposed comment correction
-        # relative paths — those links resolve only on the developer's
-        # workstation (sibling layout). CI can't validate them because the
-        # LearnStack core repo isn't checked out alongside; the audit skips
-        # `../LearnStack/` paths explicitly.
+        # Sibling-relative `../LearnStack/` documentation links are invalid
+        # on GitHub. The audit rejects them; use absolute GitHub URLs instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 174 - 178, Update the link-audit
comment near the link validation logic to state that ../LearnStack/
documentation paths are rejected, not skipped. Clarify that documentation links
must use absolute GitHub URLs and that sibling-relative paths are reserved for
filesystem or shell usage; keep the audit behavior in the relevant validation
block unchanged.
.claude/skills/commit-and-pr/SKILL.md-43-51 (1)

43-51: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the trailer for the actual contributing agent.

The skill hard-codes Claude Opus 5 and lists only Codex as an alternative. Select the Co-Authored-By value from the agent that materially contributed to the commit. Add one trailer per contributing agent.

Based on learnings, use the appropriate Co-Authored-By commit trailer for the contributing agent, with one trailer per agent when multiple agents materially contribute.

Proposed wording
-- **trailer:** `Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>` (or `Codex …` for Codex sessions). HEREDOC every multi-line message.
+- **trailer:** Add one `Co-Authored-By` trailer for each AI agent that materially contributed, using that agent's actual name and email. HEREDOC every multi-line message.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/commit-and-pr/SKILL.md around lines 43 - 51, Update the
commit trailer guidance in the commit workflow example to require the actual
materially contributing agent’s identity rather than hard-coding Claude Opus 5
or only mentioning Codex. Preserve the HEREDOC format and instruct contributors
to add one Co-Authored-By trailer for each agent that materially contributed.

Source: Learnings

docs/roadmap/P02c-1-implementation-prompt.md-6-6 (1)

6-6: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use LearnStack-Hub for the working-repository name.

The prompt uses learnstack-hub in a filesystem-path instruction. Use LearnStack-Hub/ so copied commands and path checks work on case-sensitive systems.

As per coding guidelines and retrieved learnings, sibling filesystem paths must use exact LearnStack/ and LearnStack-Hub/ capitalization.

Proposed fix
-> **The agent runs from `learnstack-hub` root.**
+> **The agent runs from `LearnStack-Hub` root.**
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/P02c-1-implementation-prompt.md` at line 6, Update the
working-repository path reference in the prompt to use the exact case-sensitive
name LearnStack-Hub/ instead of learnstack-hub, while preserving the existing
sibling LearnStack/ path.

Sources: Coding guidelines, Learnings

docs/roadmap/p02c-2-internal-api-and-contract.md-37-37 (1)

37-37: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Qualify the no-configuration-secret claim.

Line 37 says that none of the three secrets appears in configuration files. .env.example contains the development-only HUB_INTERNAL_API_HMAC_KEY placeholder. State that production secret material must not appear in configuration, logs, errors, or traces, and identify the example value as non-secret development data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/p02c-2-internal-api-and-contract.md` at line 37, Update the
documentation statement around the three secrets to qualify that production
secret material must not appear in configuration files, logs, error messages, or
traces. Explicitly identify the .env.example HUB_INTERNAL_API_HMAC_KEY
placeholder as non-secret, development-only data.
docs/roadmap/p02c-1-hub-domain-core.md-28-28 (1)

28-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

State the pipeline count as behaviors plus handler.

Line 28 calls the chain “six-step” and includes six behaviors plus Handler. docs/architecture/cross-cutting-foundation.md Line 47 counts six behaviors and Line 59 lists Handler separately. Change this to “six MediatR behaviors, followed by the Handler” to prevent test and implementation count mismatches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/p02c-1-hub-domain-core.md` at line 28, Update the MediatR
pipeline description to say “six MediatR behaviors, followed by the Handler,”
while preserving the listed behavior order and existing status details.
docs/roadmap/p02c-0-repository-bootstrap.md-50-50 (1)

50-50: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Reconcile the architecture-test count.

The Scope section names three placeholder tests. The Deliverables section claims four architecture tests and one smoke test. Name the fourth architecture test or change the count. This delivery record should define one test inventory.

Also applies to: 64-64

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/p02c-0-repository-bootstrap.md` at line 50, Reconcile the test
inventory in the roadmap document: align the Deliverables count with the three
tests listed in the Scope, or explicitly name the missing fourth architecture
test. Ensure the Scope and Deliverables sections consistently describe the same
architecture-test and smoke-test totals.
docs/architecture/entitlement-projection.md-142-149 (1)

142-149: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Remove the superseded fallback rule.

Line 149 still says that the system enters read-only mode past grace_until. Lines 142-147 define the current per-feature fail-open or fail-closed behavior and the no-throw contract. Remove the old sentence so readers do not implement the superseded mode.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/entitlement-projection.md` around lines 142 - 149, Remove
the superseded “Past `grace_until` → read-only mode” sentence from the
`HubEntitlementProvider` unreachable read-path documentation, while preserving
the current per-feature-key fail-open/fail-closed behavior and no-throw
contract.
docs/architecture/repository-layout.md-3-6 (1)

3-6: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use absolute URLs for cross-repository documentation links.

The text says that cross-repository links use ../../../LearnStack/.... Replace this with the absolute GitHub URL convention. Keep relative paths only for filesystem paths or shell commands.

As per coding guidelines, cross-repository Markdown links must use absolute GitHub URLs; sibling-relative paths are reserved for shell commands and filesystem paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/repository-layout.md` around lines 3 - 6, Update the
cross-repository link guidance in the repository-layout documentation to require
absolute GitHub URLs instead of sibling-relative ../../../LearnStack/... links.
Preserve relative paths only for filesystem paths and shell commands, and retain
the documented repository capitalization.

Source: Coding guidelines

docs/architecture/repository-layout.md-12-12 (1)

12-12: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language identifier to the tree fence.

markdownlint-cli2 reports MD040 at Line 12. Add text to the opening fence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/repository-layout.md` at line 12, Update the code fence in
the repository layout documentation to specify the text language identifier on
its opening fence, resolving the MD040 markdownlint violation.

Source: Linters/SAST tools

docs/architecture/repository-layout.md-119-122 (1)

119-122: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Classify PlanTier as an enum, not an aggregate.

docs/modules/plans.md Line 15 defines PlanTier as an enum. The planned topology lists it under Aggregates. List only Plan, or add a separate enum/value-type column.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/repository-layout.md` around lines 119 - 122, Update the
Plans entry in the repository layout so the aggregate column lists only Plan,
removing PlanTier from the aggregate classification. Preserve the existing
module and specification status information.
docs/modules/README.md-39-41 (1)

39-41: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Match the audit-coverage statement to the module documents.

This section says every module document has a MUST/SHOULD/MAY table. docs/modules/entitlements.md Lines 54-56 uses prose for its Audit coverage section. Add the matrix there, or change this statement to allow prose.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/modules/README.md` around lines 39 - 41, Align the “Audit-coverage
matrices” statement with the module documentation by updating
docs/modules/entitlements.md’s “Audit coverage” section to use the same
MUST/SHOULD/MAY matrix format, or revise the statement to explicitly permit
prose; keep the documented audit requirements consistent with the existing
entitlements content.
🧹 Nitpick comments (4)
docs/roadmap/hub-marketplace.md (2)

3-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Link the migrated LearnStack roadmap file.

This paragraph names a sibling-repository document without a navigable link. Replace the bare filename with the canonical absolute GitHub URL.

Proposed documentation fix
-> `phase-12-hub-marketplace.md`, which is now a pointer at this file.
+> [`phase-12-hub-marketplace.md`](https://github.com/HodeTech/LearnStack/blob/main/docs/roadmap/phase-12-hub-marketplace.md), which is now a pointer at this file.

As per coding guidelines, cross-repo references to LearnStack documentation must use absolute GitHub URLs; sibling-relative paths are reserved for shell commands and filesystem paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/hub-marketplace.md` around lines 3 - 6, Update the introductory
paragraph near “This is the authoritative plan” to replace the bare LearnStack
filename phase-12-hub-marketplace.md with its canonical absolute GitHub URL,
preserving the surrounding wording and formatting.

Source: Coding guidelines


68-90: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assign the collision-resolution ADR to the cross-repository decision owner.

This ADR changes ADR-0034's Hub/LearnStack invariant. The roadmap says an ADR is required but does not identify LearnStack/docs/decisions/ as the authoritative location. A Hub-only decision would create a second source of truth for a cross-repository boundary. State the owner and require both roadmaps to reference the accepted ADR.

As per coding guidelines, cross-cutting architectural decisions belong in ../LearnStack/docs/decisions/; Hub-only decisions use the HUB-NNNN series.

Also applies to: 198-199

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/hub-marketplace.md` around lines 68 - 90, The roadmap’s
collision-resolution ADR must be owned by the cross-repository decision owner
and recorded authoritatively in ../LearnStack/docs/decisions/ rather than as a
Hub-only decision. Update the “unresolved collision with ADR-0034” section to
state this location and require both Hub and LearnStack roadmaps to reference
the accepted ADR; do not use the HUB-NNNN series.

Source: Coding guidelines

docs/roadmap/p02c-5-custom-domain-lifecycle.md (1)

199-202: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Document Hub-first coordination for the paired implementation.

“Same session” does not identify which repository owns the canonical contract. State that the Hub PR opens first, the LearnStack PR references the Hub commit, and both PRs merge in the same session.

Based on learnings, cross-repository changes must coordinate both PRs: the Hub PR opens first with the canonical contract, the LearnStack PR references its commit, and both merge in the same session.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/p02c-5-custom-domain-lifecycle.md` around lines 199 - 202,
Update the cross-repository coordination note around the LearnStack-side event
consumer, host-mappings handler, and platform_host_to_tenant writes to state
that the Hub PR opens first and establishes the canonical contract, the
LearnStack PR references the Hub commit, and both PRs merge in the same session.

Source: Learnings

docs/roadmap/P02c-1-implementation-prompt.md (1)

137-137: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Require one co-author trailer per contributing agent.

If multiple agents materially contribute, this singular rule can omit contributors. Require one Co-Authored-By trailer for each materially contributing agent and use each agent's actual identity.

Based on learnings, commit attribution must include one Co-Authored-By trailer per materially contributing agent.

Suggested wording
-**English docs; Conventional Commits; AI co-author trailer** (`Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>`).
+**English docs; Conventional Commits; one AI co-author trailer per materially contributing agent** (`Co-Authored-By: <agent identity>`).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/roadmap/P02c-1-implementation-prompt.md` at line 137, Update the
commit-attribution guidance in the roadmap document to require one
Co-Authored-By trailer for every materially contributing agent, using each
agent’s actual identity rather than a single fixed identity. Preserve the
existing English documentation and Conventional Commits requirements.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/add-hub-module/SKILL.md:
- Line 18: Update the module description in the add-hub-module skill to remove
the normative link and dependency on LearnStack's add-backend-module skill.
Describe or reference the stable module pattern using this repository's
.claude/skills/ content or a standard architecture document, while preserving
the Hub-specific exclusions such as tenant isolation.

In `@AGENTS.md`:
- Around line 18-24: Remove the duplicated shared skill-catalogue, workflow,
sibling-path, URL, and entry-point guidance from AGENTS.md. Keep only the
agent-specific trailer differences described by the file, and preserve the
redirect structure; make any shared-guidance updates in CLAUDE.md instead.

In `@backend/src/Core/LearnStack.Hub.Api/Program.cs`:
- Around line 7-9: Update the pipeline documentation comment near the six-step
behavior contract to state that Hub includes the OutboxFlushBehavior shell,
while still excluding TenantContextBehavior. Ensure the wording does not imply
that OutboxFlushBehavior is absent or may be omitted.

In `@CLAUDE.md`:
- Line 58: Replace cross-repository LearnStack documentation references with
absolute GitHub URLs: update CLAUDE.md lines 58-58 to state that skills cite
LearnStack standards and ADRs by absolute GitHub URL, update
.claude/skills/README.md lines 5-5 to use absolute URLs instead of sibling
paths, and update .github/pull_request_template.md lines 5-5 to use an absolute
ADR URL placeholder. Retain sibling-relative paths only for shell commands and
filesystem locations.

In `@docs/glossary.md`:
- Line 17: Align the glossary entry with the defined packet scope: either remove
LicenseKey from the Entitlement Aggregate inputs and recomputation triggers for
the P02c-1 claim, or explicitly state that LicenseKey belongs to a later packet
and update the authoritative entitlement projection and recomputation-contract
documents consistently. Keep the P02c-1 dependency from including the P02c-6
module unless those documents are intentionally revised together.

In `@docs/modules/entitlements.md`:
- Line 50: Update the EntitlementProjection_Shape_IsStable documentation to make
the contract snapshot mandatory in both LearnStack and Hub repositories. Name
the checked-in entitlement-v1.schema.json and corresponding snapshot test in
each repository, and state that both snapshots must be updated together to
preserve the LearnStack↔Hub wire shape.

In `@docs/roadmap/hub-billing.md`:
- Around line 15-18: Update the P02c-1 and P02c-2 completion wording in the
roadmap to match the authoritative status ledger: replace claims that P02c-1
“shipped” or that usage is already handled by P02c-2 with neutral “defines” and
future-oriented “will be handled by P02c-2” wording, including the corresponding
passage at the other referenced section.
- Around line 135-143: Update the Naming section to place Stripe SDK adapters in
LearnStack.Hub.Infrastructure.Stripe and Iyzico SDK adapters in
LearnStack.Hub.Infrastructure.Iyzico, with the manual adapter documented
separately under its own project. Preserve the distinction from LearnStack
core’s IPaymentProvider and ensure the documented project names comply with the
SDK import boundary.

In `@docs/roadmap/p02c-2-internal-api-and-contract.md`:
- Around line 31-37: The authentication section should treat ADR-0034 as the
current authority instead of describing the layers as unchanged by ADR-0034 or
relying on ADR-0019. Update the citation and wording around the mTLS, RS256 JWT,
and HMAC requirements while preserving that three-layer chain and its stated
secret-handling and rejection behavior.
- Around line 51-56: Clarify the durable dispatch contract in the projection
recomputation section and the requirement at line 89: specify whether a single
outbox record coordinates both the HTTP projection push and the
learnstack.hub.entitlement event, or whether each operation has its own outbox
record and handler. Define independent idempotency and retry behavior for both
deliveries, ensuring neither can be omitted and that partial failures converge
safely.

In `@docs/roadmap/p02c-4-operator-portal.md`:
- Around line 38-43: Align the MFA documentation and integration-test references
with the configuration model and authoritative realm export defined in
infra/keycloak/README.md: consistently state whether CONFIGURE_TOTP applies to
every user or an operator realm role, identify the single owning export file,
and reference one integration-test path that validates that export and enforced
OTP behavior.

In `@docs/roadmap/p02c-5-custom-domain-lifecycle.md`:
- Around line 35-43: The custom-domain lifecycle state diagram must not
transition from Verifying directly to Active on MarkVerified. Add an explicit
propagation state after certificate verification, require successful
host-mapping push and secret replication acknowledgements before entering
Active, and emit the activated event only on that final transition; update the
corresponding lifecycle descriptions and examples consistently.
- Around line 84-87: Update the recurring verification job description to select
validation by challenge mode: the default DNS-01 flow must poll the
_acme-challenge TXT record, while the CNAME mode must perform CNAME
verification. Specify coverage for both TXT and CNAME paths in the associated
tests.
- Around line 219-223: Update the custom-domain lifecycle documentation around
the inbound POST endpoint to name the approved LearnStack-to-Hub adapter, using
IHubTenantSync if that is the applicable contract, and define its
request/response contract before implementation. Make the Hub client boundary
explicit while preserving the existing Admin Studio proxy flow.

In `@docs/roadmap/p02c-7-exit-gate.md`:
- Around line 45-50: Update the LearnStack projection acceptance logic described
in the PUT /api/internal/tenants/{id}/entitlements flow: accept higher
generations, accept equal-generation payloads only when they are identical to
the cached projection, and return a conflict for divergent equal-generation
payloads. Add coverage for a divergent equal-generation replay while preserving
idempotent duplicate delivery.
- Around line 76-94: Clarify Gate 4’s test-count wording so “three” explicitly
refers to the three authentication legs, not three total test cases. Require
separate negative tests for every listed JWT variant (absent, expired, wrong
audience, replayed jti) and HMAC variant (absent, tampered body), across both
internal surfaces and directions.

In `@docs/roadmap/README.md`:
- Line 9: Update the roadmap bullet describing the P02c-1 unfreeze condition to
include both requirements: the ADR-0035 billing or plan-gating trigger and
reconciliation with ADR-0033 and ADR-0034 before frozen P02c-1 code can merge.
Do not narrow the gate to ADR-0035 alone.

---

Minor comments:
In @.claude/skills/commit-and-pr/SKILL.md:
- Around line 43-51: Update the commit trailer guidance in the commit workflow
example to require the actual materially contributing agent’s identity rather
than hard-coding Claude Opus 5 or only mentioning Codex. Preserve the HEREDOC
format and instruct contributors to add one Co-Authored-By trailer for each
agent that materially contributed.

In @.github/workflows/ci.yml:
- Around line 174-178: Update the link-audit comment near the link validation
logic to state that ../LearnStack/ documentation paths are rejected, not
skipped. Clarify that documentation links must use absolute GitHub URLs and that
sibling-relative paths are reserved for filesystem or shell usage; keep the
audit behavior in the relevant validation block unchanged.

In `@docs/architecture/entitlement-projection.md`:
- Around line 142-149: Remove the superseded “Past `grace_until` → read-only
mode” sentence from the `HubEntitlementProvider` unreachable read-path
documentation, while preserving the current per-feature-key
fail-open/fail-closed behavior and no-throw contract.

In `@docs/architecture/repository-layout.md`:
- Around line 3-6: Update the cross-repository link guidance in the
repository-layout documentation to require absolute GitHub URLs instead of
sibling-relative ../../../LearnStack/... links. Preserve relative paths only for
filesystem paths and shell commands, and retain the documented repository
capitalization.
- Line 12: Update the code fence in the repository layout documentation to
specify the text language identifier on its opening fence, resolving the MD040
markdownlint violation.
- Around line 119-122: Update the Plans entry in the repository layout so the
aggregate column lists only Plan, removing PlanTier from the aggregate
classification. Preserve the existing module and specification status
information.

In `@docs/modules/README.md`:
- Around line 39-41: Align the “Audit-coverage matrices” statement with the
module documentation by updating docs/modules/entitlements.md’s “Audit coverage”
section to use the same MUST/SHOULD/MAY matrix format, or revise the statement
to explicitly permit prose; keep the documented audit requirements consistent
with the existing entitlements content.

In `@docs/roadmap/p02c-0-repository-bootstrap.md`:
- Line 50: Reconcile the test inventory in the roadmap document: align the
Deliverables count with the three tests listed in the Scope, or explicitly name
the missing fourth architecture test. Ensure the Scope and Deliverables sections
consistently describe the same architecture-test and smoke-test totals.

In `@docs/roadmap/p02c-1-hub-domain-core.md`:
- Line 28: Update the MediatR pipeline description to say “six MediatR
behaviors, followed by the Handler,” while preserving the listed behavior order
and existing status details.

In `@docs/roadmap/P02c-1-implementation-prompt.md`:
- Line 6: Update the working-repository path reference in the prompt to use the
exact case-sensitive name LearnStack-Hub/ instead of learnstack-hub, while
preserving the existing sibling LearnStack/ path.

In `@docs/roadmap/p02c-2-internal-api-and-contract.md`:
- Line 37: Update the documentation statement around the three secrets to
qualify that production secret material must not appear in configuration files,
logs, error messages, or traces. Explicitly identify the .env.example
HUB_INTERNAL_API_HMAC_KEY placeholder as non-secret, development-only data.

In `@docs/roadmap/p02c-4-operator-portal.md`:
- Around line 122-131: Standardize all repository-root references to the
operator portal on frontend/apps/operator-portal, including the later
apps/operator-portal mention in this section. If retaining apps/operator-portal,
explicitly identify it as frontend-workspace-relative.

In `@README.md`:
- Around line 30-34: Add the text language identifier to the directory-layout
fenced block in README.md by changing its opening fence to use ```text, while
leaving the directory tree content unchanged.

---

Nitpick comments:
In `@docs/roadmap/hub-marketplace.md`:
- Around line 3-6: Update the introductory paragraph near “This is the
authoritative plan” to replace the bare LearnStack filename
phase-12-hub-marketplace.md with its canonical absolute GitHub URL, preserving
the surrounding wording and formatting.
- Around line 68-90: The roadmap’s collision-resolution ADR must be owned by the
cross-repository decision owner and recorded authoritatively in
../LearnStack/docs/decisions/ rather than as a Hub-only decision. Update the
“unresolved collision with ADR-0034” section to state this location and require
both Hub and LearnStack roadmaps to reference the accepted ADR; do not use the
HUB-NNNN series.

In `@docs/roadmap/P02c-1-implementation-prompt.md`:
- Line 137: Update the commit-attribution guidance in the roadmap document to
require one Co-Authored-By trailer for every materially contributing agent,
using each agent’s actual identity rather than a single fixed identity. Preserve
the existing English documentation and Conventional Commits requirements.

In `@docs/roadmap/p02c-5-custom-domain-lifecycle.md`:
- Around line 199-202: Update the cross-repository coordination note around the
LearnStack-side event consumer, host-mappings handler, and
platform_host_to_tenant writes to state that the Hub PR opens first and
establishes the canonical contract, the LearnStack PR references the Hub commit,
and both PRs merge in the same session.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 07afafc8-31c7-476e-bba4-7acfcb481fbd

📥 Commits

Reviewing files that changed from the base of the PR and between ce79798 and c2db5e8.

📒 Files selected for processing (57)
  • .claude/skills/README.md
  • .claude/skills/add-architecture-test/SKILL.md
  • .claude/skills/add-ef-migration/SKILL.md
  • .claude/skills/add-feature-key/SKILL.md
  • .claude/skills/add-hub-module/SKILL.md
  • .claude/skills/add-integration-event/SKILL.md
  • .claude/skills/code-review/SKILL.md
  • .claude/skills/commit-and-pr/SKILL.md
  • .claude/skills/implement-task/SKILL.md
  • .claude/skills/local-dev-setup/SKILL.md
  • .claude/skills/standards-check/SKILL.md
  • .claude/skills/start-task/SKILL.md
  • .claude/skills/update-glossary/SKILL.md
  • .claude/skills/wire-cross-cutting-foundation/SKILL.md
  • .claude/skills/write-adr/SKILL.md
  • .env.example
  • .github/pull_request_template.md
  • .github/workflows/ci.yml
  • AGENTS.md
  • CLAUDE.md
  • CONTRIBUTING.md
  • README.md
  • backend/src/Core/LearnStack.Hub.Api/Program.cs
  • backend/src/Modules/README.md
  • backend/tests/LearnStack.Hub.Tests.Architecture/HubBoundaryTests.cs
  • backend/tests/LearnStack.Hub.Tests.Architecture/RepositoryLayoutTests.cs
  • docs/README.md
  • docs/architecture/README.md
  • docs/architecture/contract-with-learnstack.md
  • docs/architecture/cross-cutting-foundation.md
  • docs/architecture/entitlement-projection.md
  • docs/architecture/module-topology.md
  • docs/architecture/repository-layout.md
  • docs/decisions/README.md
  • docs/glossary.md
  • docs/modules/README.md
  • docs/modules/entitlements.md
  • docs/modules/plans.md
  • docs/modules/subscriptions.md
  • docs/modules/tenant-lifecycle.md
  • docs/operations/README.md
  • docs/roadmap/P02c-1-implementation-prompt.md
  • docs/roadmap/README.md
  • docs/roadmap/hub-billing.md
  • docs/roadmap/hub-marketplace.md
  • docs/roadmap/p02c-0-repository-bootstrap.md
  • docs/roadmap/p02c-1-hub-domain-core.md
  • docs/roadmap/p02c-2-internal-api-and-contract.md
  • docs/roadmap/p02c-3-learnstack-integration.md
  • docs/roadmap/p02c-4-operator-portal.md
  • docs/roadmap/p02c-5-custom-domain-lifecycle.md
  • docs/roadmap/p02c-6-license-key.md
  • docs/roadmap/p02c-7-exit-gate.md
  • infra/compose/README.md
  • infra/compose/dev.yml
  • infra/keycloak/README.md
  • scripts/seed.sh

Comment thread .claude/skills/add-hub-module/SKILL.md
Comment thread AGENTS.md
Comment thread backend/src/Core/LearnStack.Hub.Api/Program.cs Outdated
Comment thread CLAUDE.md
Comment thread docs/glossary.md
Comment thread docs/roadmap/p02c-5-custom-domain-lifecycle.md
Comment thread docs/roadmap/p02c-5-custom-domain-lifecycle.md
Comment thread docs/roadmap/p02c-7-exit-gate.md
Comment thread docs/roadmap/p02c-7-exit-gate.md
Comment thread docs/roadmap/README.md Outdated
… P02c-2

P02c-1 merged to `main` while this branch was open, so the branch's central
claim — that the Hub domain core "exists and carries the Hub domain core,
but it is not merged" — stopped being true. Rather than pick a winner
between the two, this reconciles them on the distinction that actually
matters: **the freeze is on the track's forward motion, not on the merged
artefact.**

That reframe came out of a review of PR #1's 221 files against the three
decisions the restructure moved. It implements none of them:

- ADR-0034 (endpoint set, auth chain) — no LearnStackApiClient, no hosted
  /api/internal/*, both deferred to P02c-2. Nothing to be stale.
- ADR-0033 (audit durability) — AuditLogBehavior is an explicit shell, and
  the Hub has no IAuditStore and no audit_log table at all.
- ADR-0035 (demand-gating) — a scheduling decision, not code.

And the one cross-repo artefact it does ship was already correct:
entitlement-v1.schema.json carries `grace_until` and `generation` with the
wire names `tier` and `expires_at`, and carries no certificate, key, host
or domain field. That is the same shape LearnStack's side was aligned to,
so the snapshot test the freeze was written to protect is the thing that
already matched.

Freezing the merged artefact would therefore have bought nothing and cost
the SharedKernel reconciliation against LearnStack Packet 3b — which,
unlike LearnStack's, already has four modules of consumers and grows with
every packet built on top.

Four conflicts, resolved:

- CLAUDE.md and docs/roadmap/README.md — rewritten to the reframe rather
  than to either side. P02c-1 ✅ Shipped; P02c-2 onward ⏸ Frozen on the
  ADR-0035 trigger. main's "Deferred from P02c-1" follow-ups are preserved
  — they are real tracked work, not status prose.
- Program.cs — main's real implementation kept, its TODO replaced. The old
  one said "wire the four-endpoint contract surface" and named four of ten
  crossings, omitting host-mappings, which is the endpoint that exists so
  certificate material stops riding the entitlement payload. An
  implementer following it would have rebuilt the defect ADR-0034 removed.
- scripts/seed.sh — main's relaxed pre-flight kept; it is correct now that
  the in-process seeder landed and needs only Postgres.

One defect merged silently, with no conflict marker, and is fixed here:
ci.yml's header said `backend-integration` is `if: false` until P02c-2
while the job below it ran ungated. P02c-1 shipped the first
Testcontainers tests and activating it there is the right call, so the
five carriers that said P02c-2 owns the activation are corrected — except
P02c-0's delivery record, which was true when written.

Also corrected: three source comments claiming Hub drops "the two
tenant-isolation steps" from "core's 8-step pipeline". Hub drops exactly
one behavior, TenantContextBehavior. Seven behaviors against six, with the
convention named so the arithmetic stops going wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/Core/LearnStack.Hub.Api/Program.cs (1)

31-38: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail closed for an invalid deployment mode.

When Hub:DeploymentMode is missing or invalid, this code selects DeploymentMode.Development and can register NoOpErrorTracker in production. Enum.TryParse also accepts undefined numeric values, which bypass the intended validation. Allow the missing value only when builder.Environment.IsDevelopment(); otherwise require a defined DeploymentMode and throw on invalid input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/Core/LearnStack.Hub.Api/Program.cs` around lines 31 - 38, Update
the deploymentMode initialization at the composition root to fail closed: allow
a missing Hub:DeploymentMode only when builder.Environment.IsDevelopment(),
otherwise throw. Parse configured values case-insensitively and require
Enum.IsDefined for the parsed DeploymentMode so undefined numeric values are
rejected; do not fall back to Development for invalid or missing production
configuration.
🧹 Nitpick comments (1)
backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs (1)

8-28: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Verify the actual MediatR registration order.

The current test checks only CanonicalBehaviorOrder. It does not call AddHubMediatRPipeline or execute a request, so the registered behavior order can diverge while the test and XML documentation continue to pass.

  • backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs#L8-L28: add a service-provider or probe-request assertion for the resolved behavior order.
  • backend/src/Core/LearnStack.Hub.Application/Pipeline/MediatRPipelineRegistration.cs#L12-L14: narrow the XML claim until the DI/execution assertion exists.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs` around
lines 8 - 28, The test at
backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs:8-28 must
verify the actual order produced by AddHubMediatRPipeline, using a service
provider or probe request to assert resolved/executed behaviors match the
canonical sequence; checking only
MediatRPipelineRegistration.CanonicalBehaviorOrder is insufficient. Until that
runtime assertion exists, narrow the XML documentation claim in
backend/src/Core/LearnStack.Hub.Application/Pipeline/MediatRPipelineRegistration.cs:12-14
so it describes the declared canonical order without claiming the registered
pipeline is verified.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/roadmap/p02c-1-hub-domain-core.md`:
- Around line 123-132: Update the closing P02c-2 gate statement to require both
conditions before unfreezing: the ADR-0035 billing or plan-gating trigger must
fire and the contract surface must comply with ADR-0034. Keep P02c-2 and
downstream work explicitly frozen until both conditions are satisfied.

In `@docs/roadmap/README.md`:
- Around line 19-21: Reconcile P02c-1 to one authoritative merge state across
all records: update the packet table at docs/roadmap/README.md:19-21, the
closure record and ledger date at docs/roadmap/README.md:171-171, and the
status/merge record at docs/roadmap/p02c-1-hub-domain-core.md:3-5 so they
consistently reflect whether the packet is merged or remains branch-held and
frozen. Do not leave conflicting shipped and unmerged claims.

---

Outside diff comments:
In `@backend/src/Core/LearnStack.Hub.Api/Program.cs`:
- Around line 31-38: Update the deploymentMode initialization at the composition
root to fail closed: allow a missing Hub:DeploymentMode only when
builder.Environment.IsDevelopment(), otherwise throw. Parse configured values
case-insensitively and require Enum.IsDefined for the parsed DeploymentMode so
undefined numeric values are rejected; do not fall back to Development for
invalid or missing production configuration.

---

Nitpick comments:
In `@backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs`:
- Around line 8-28: The test at
backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs:8-28 must
verify the actual order produced by AddHubMediatRPipeline, using a service
provider or probe request to assert resolved/executed behaviors match the
canonical sequence; checking only
MediatRPipelineRegistration.CanonicalBehaviorOrder is insufficient. Until that
runtime assertion exists, narrow the XML documentation claim in
backend/src/Core/LearnStack.Hub.Application/Pipeline/MediatRPipelineRegistration.cs:12-14
so it describes the declared canonical order without claiming the registered
pipeline is verified.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d628f863-5891-41a4-8fad-f6cbb209047d

📥 Commits

Reviewing files that changed from the base of the PR and between c2db5e8 and 76f0f45.

📒 Files selected for processing (13)
  • .github/workflows/ci.yml
  • CLAUDE.md
  • CONTRIBUTING.md
  • README.md
  • backend/src/Core/LearnStack.Hub.Api/Program.cs
  • backend/src/Core/LearnStack.Hub.Application/LearnStack.Hub.Application.csproj
  • backend/src/Core/LearnStack.Hub.Application/Pipeline/MediatRPipelineRegistration.cs
  • backend/tests/LearnStack.Hub.Tests.Architecture/HubBoundaryTests.cs
  • backend/tests/LearnStack.Hub.Tests.Architecture/PipelineOrderTests.cs
  • docs/architecture/repository-layout.md
  • docs/roadmap/README.md
  • docs/roadmap/p02c-1-hub-domain-core.md
  • docs/roadmap/p02c-2-internal-api-and-contract.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • backend/tests/LearnStack.Hub.Tests.Architecture/HubBoundaryTests.cs
  • CONTRIBUTING.md
  • .github/workflows/ci.yml
  • README.md
  • docs/roadmap/p02c-2-internal-api-and-contract.md
  • docs/architecture/repository-layout.md
  • CLAUDE.md

Comment thread docs/roadmap/p02c-1-hub-domain-core.md Outdated
Comment thread docs/roadmap/README.md
cemililik and others added 3 commits August 9, 2026 15:41
Standards 21's "Awaiting backfill" section now reads "Every identifier
previously parked in this section has been folded into the catalogue above
under its canonical name", and the Hub-side tests carry full entries there.
This pointer file still told a Hub-side reader they were pending.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Caught by CodeRabbit on the PR, and it is the exact class of defect this
restructure exists to eliminate: one file stating two opposite things about
the same packet.

`docs/roadmap/README.md` line 19 says "P02c-1 — ✅ Shipped", line 29 says
"P02c-1 is **merged**", and line 165 — the artefact status ledger further
down the same file — said "On branch, **frozen** … Not merged." The earlier
sweep matched on "P02c-1 is frozen" and "❄️" and never saw "On branch,
**frozen**".

Four more carriers of the same stale state, all missed for the same reason:

- The ledger row for everything after P02c-1 said "Not started" where the
  distinction that matters is *frozen* — not started is a schedule, frozen
  is a decision.
- "P02c-0, P02c-1, P02c-2 and P02c-4 are unblocked by LearnStack" was
  written before the merge and still listed two shipped packets among the
  work that can proceed.
- `repository-layout.md` said `backend/src/Modules/` "is still empty of
  module subdirectories" and that the branch filling it is frozen. Four
  modules are on `main`. Its tree comment and its "NOT YET ON DISK" heading
  said the same.

Also, the governance bullet said the ADR-0035 trigger "is what governs when
this plan resumes" — one condition — while CLAUDE.md states two, the second
being that the contract surface is built to ADR-0034's invariants rather
than an endpoint count. The bullet now names both and points at the same
place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CLAUDE.md and the roadmap README both say P02c-2 unfreezes when two
conditions hold; this packet's exit decision named only the ADR-0035
trigger. A reader following it would start P02c-2 with the trigger fired
and build the contract surface to an endpoint count — the thing ADR-0034
exists to stop.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/architecture/repository-layout.md (1)

3-6: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use absolute GitHub URLs for cross-repository documentation links.

Line 6 describes ../../../LearnStack/... as a cross-repository link. Use that path only for local filesystem or shell examples. Use an absolute GitHub URL for Markdown documentation links.

Suggested wording
- with those exact capitalisations; cross-repo links from this file are written
- `../../../LearnStack/...`.
+ with those exact capitalisations; cross-repository Markdown links use absolute
+ GitHub URLs. Sibling paths such as `../../../LearnStack/...` are for local
+ filesystem and shell examples only.

As per coding guidelines, **/*.md files must use absolute GitHub URLs for cross-repository LearnStack documentation links; sibling-relative paths are allowed only for local shell commands and filesystem paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/architecture/repository-layout.md` around lines 3 - 6, Update the
cross-repository Markdown link guidance in repository-layout.md to use an
absolute GitHub URL for LearnStack documentation references, while retaining
../../../LearnStack/... only in local filesystem or shell examples.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/architecture/repository-layout.md`:
- Around line 8-11: Reconcile the P02c-1 documentation with the implemented
state of TenantLifecycle, Plans, Subscriptions, and Entitlements: remove or
update stale “only file on disk,” “unfreezes,” “ahead of implementation,” and
“spec written” claims in docs/architecture/repository-layout.md at lines 8-11,
29-30, 78-79, 107-108, 114-123, and 144, and align docs/modules/README.md with
the four implemented module directories.

---

Outside diff comments:
In `@docs/architecture/repository-layout.md`:
- Around line 3-6: Update the cross-repository Markdown link guidance in
repository-layout.md to use an absolute GitHub URL for LearnStack documentation
references, while retaining ../../../LearnStack/... only in local filesystem or
shell examples.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6c8d0f83-5fbf-4c0e-876e-d1e62dd7989e

📥 Commits

Reviewing files that changed from the base of the PR and between 54d685d and 0e3041e.

📒 Files selected for processing (2)
  • docs/architecture/repository-layout.md
  • docs/roadmap/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/roadmap/README.md

Comment thread docs/architecture/repository-layout.md
@cemililik
cemililik merged commit d569da4 into main Aug 10, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant