PortalJS — Architect
Overview
The advisory entry point for a PortalJS project. Before anything gets scaffolded, this
skill works out what to build: given the kind of portal, the shape of the data, and its
purpose, it fills six architecture slots (storage, catalog, compute, access, hosting,
metadata), resolves two build-time knobs (per-dataset data tier and the portal-wide
DATA_QUERY mode), and hands off to the concrete build skills. It decides; it does not
build. When the brief is thin it interviews in short rounds and never dead-ends — every
question has a sensible default, reachable by replying "use defaults."
Prerequisites
- A rough idea of the portal's purpose and data (exact numbers are not required — the
interview supplies defaults for anything missing).
- Optional: local files or a directory of sample data to inspect for size and shape.
- No PortalJS project needs to exist yet; this skill runs before scaffolding.
Instructions
The canonical, full step-by-step workflow lives in
.claude/commands/portaljs-architect.md —
that file is the single source of truth. Follow it when executing this skill:
- Parse
$ARGUMENTS for anything already specified, then interview for what's
missing, one round at a time: (1) what's being built, (2) what the data is,
(3) what it's for, (4) constraints. Accept "use defaults" at any point. Inspect
named files/directories with du -sh and line counts to ground size guesses.
- Derive the recommendation by matching the answers against the decision tables —
Storage/Catalog/Compute by data volume and query needs, Access/Hosting by public
vs. private, Metadata by standards-compliance needs — then resolve the two
build-time knobs: per-dataset data tier (
inline | LFS | external) and the
portal-wide DATA_QUERY mode (flat | duckdb).
- Echo the architecture brief (stack, reasoning per slot, deviations from default,
deferred items) and wait for confirmation ("go") or corrections.
- On confirmation, persist the brief to
./ARCHITECTURE.md in the working directory.
- Hand off to the build skills —
/portaljs-new-portal, /portaljs-add-dataset,
/portaljs-connect-ckan, /portaljs-define-schema, /portaljs-deploy — mapped
from the brief, and offer to run the first one.
Output
- Created:
./ARCHITECTURE.md documenting the six slots, the two build-time
knobs, the reasoning, and anything deferred to a later build step.
- Modified: nothing else — this skill is advisory only.
- Verified: the brief was echoed back and confirmed before being persisted.
- Result: a concrete, named sequence of follow-up skill invocations
(e.g.
/portaljs-new-portal → /portaljs-add-dataset → /portaljs-deploy).
Error Handling
| Symptom |
Cause |
Fix |
| Skill keeps asking rounds of questions |
Brief was thin or $ARGUMENTS omitted |
Answer inline, or reply "use defaults" to accept the opinionated default stack |
| Recommendation looks generic |
Rounds were skipped without real data details |
Give actual size/shape/cadence, or point at files for du -sh inspection |
ARCHITECTURE.md never appears |
Confirmation step was skipped |
Reply "go" once the echoed brief looks right |
Scaffolded portal has the wrong DATA_QUERY |
Flat downgrade wasn't applied |
Run the perl -pi -e one-liner from the command file against lib/datasets.ts |
| Hand-off names a skill that doesn't exist |
Decision maps to a (planned) skill (e.g. /connect-openmetadata) |
Treat it as designed-in/built-later; proceed with the closest available skill |
Examples
Example 1 — National statistics office, DCAT-AP harvesting
/portaljs-architect We're a national statistics office. ~200 datasets, mostly large
CSVs (some GBs), updated quarterly, all public, and we must publish DCAT-AP for the
EU data portal.
Infers a multi-publisher, analytics-grade portal. Recommends Parquet on R2 + DuckLake +
DuckDB, static Cloudflare Pages, Frictionless + DCAT-AP metadata, owner namespace,
data tier external for the Parquet, DATA_QUERY=duckdb. Writes ARCHITECTURE.md and
hands off to /portaljs-new-portal then /portaljs-add-dataset.
Example 2 — Small nonprofit, no arguments given
/portaljs-architect
Runs the full four-round interview since nothing was pre-filled. Accepting defaults at
each round lands on the opinionated default stack: repo files or Git-LFS + R2 storage,
datasets.json catalog, DuckDB compute, static access on Cloudflare Pages, Frictionless
metadata, theme namespace, data tier LFS, DATA_QUERY=duckdb.
Example 3 — Internal catalog with restricted datasets
/portaljs-architect Internal engineering data catalog, single team, dozens of CSVs,
some of it access-controlled to specific roles.
The private-data answer in Round 2 flips Access/Hosting to runtime + backend RBAC on
Cloudflare Workers — flagged as the larger, opt-in build — while Storage/Catalog/Compute
still follow the volume-based defaults.
Resources
Source: jeremylongshore/claude-code-plugins-plus-skills → plugins/community/portaljs/skills/portaljs-architect/SKILL.md
1---2name: portaljs-architect3description: "Recommend a data-portal architecture (storage, compute, catalog, access, hosting, metadata) from stated needs, then hand off to the build skills. The advisory entry point. Use when starting a new data-portal project and the underlying architecture has not yet been decided."4allowed-tools: "Read, Write, Bash(du:*), Bash(wc:*), Bash(ls:*), Bash(head:*)"5category: engineering-core6source_repo: jeremylongshore/claude-code-plugins-plus-skills7source_path: "plugins/community/portaljs/skills/portaljs-architect/SKILL.md"8source_url: https://github.com/jeremylongshore/claude-code-plugins-plus-skills/blob/HEAD/plugins/community/portaljs/skills/portaljs-architect/SKILL.md9---101112# PortalJS — Architect1314## Overview15The advisory entry point for a PortalJS project. Before anything gets scaffolded, this16skill works out what to build: given the kind of portal, the shape of the data, and its17purpose, it fills six architecture slots (storage, catalog, compute, access, hosting,18metadata), resolves two build-time knobs (per-dataset data tier and the portal-wide19`DATA_QUERY` mode), and hands off to the concrete build skills. It decides; it does not20build. When the brief is thin it interviews in short rounds and never dead-ends — every21question has a sensible default, reachable by replying "use defaults."2223## Prerequisites24- A rough idea of the portal's purpose and data (exact numbers are not required — the25 interview supplies defaults for anything missing).26- Optional: local files or a directory of sample data to inspect for size and shape.27- No PortalJS project needs to exist yet; this skill runs before scaffolding.2829## Instructions30The canonical, full step-by-step workflow lives in31[`.claude/commands/portaljs-architect.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-architect.md) —32that file is the single source of truth. Follow it when executing this skill:33341. Parse `$ARGUMENTS` for anything already specified, then interview for what's35 missing, one round at a time: (1) what's being built, (2) what the data is,36 (3) what it's for, (4) constraints. Accept "use defaults" at any point. Inspect37 named files/directories with `du -sh` and line counts to ground size guesses.382. Derive the recommendation by matching the answers against the decision tables —39 Storage/Catalog/Compute by data volume and query needs, Access/Hosting by public40 vs. private, Metadata by standards-compliance needs — then resolve the two41 build-time knobs: per-dataset **data tier** (`inline | LFS | external`) and the42 portal-wide **`DATA_QUERY`** mode (`flat | duckdb`).433. Echo the architecture brief (stack, reasoning per slot, deviations from default,44 deferred items) and wait for confirmation ("go") or corrections.454. On confirmation, persist the brief to `./ARCHITECTURE.md` in the working directory.465. Hand off to the build skills — `/portaljs-new-portal`, `/portaljs-add-dataset`,47 `/portaljs-connect-ckan`, `/portaljs-define-schema`, `/portaljs-deploy` — mapped48 from the brief, and offer to run the first one.4950## Output51- **Created:** `./ARCHITECTURE.md` documenting the six slots, the two build-time52 knobs, the reasoning, and anything deferred to a later build step.53- **Modified:** nothing else — this skill is advisory only.54- **Verified:** the brief was echoed back and confirmed before being persisted.55- **Result:** a concrete, named sequence of follow-up skill invocations56 (e.g. `/portaljs-new-portal` → `/portaljs-add-dataset` → `/portaljs-deploy`).5758## Error Handling59| Symptom | Cause | Fix |60| --- | --- | --- |61| Skill keeps asking rounds of questions | Brief was thin or `$ARGUMENTS` omitted | Answer inline, or reply "use defaults" to accept the opinionated default stack |62| Recommendation looks generic | Rounds were skipped without real data details | Give actual size/shape/cadence, or point at files for `du -sh` inspection |63| `ARCHITECTURE.md` never appears | Confirmation step was skipped | Reply "go" once the echoed brief looks right |64| Scaffolded portal has the wrong `DATA_QUERY` | Flat downgrade wasn't applied | Run the `perl -pi -e` one-liner from the command file against `lib/datasets.ts` |65| Hand-off names a skill that doesn't exist | Decision maps to a *(planned)* skill (e.g. `/connect-openmetadata`) | Treat it as designed-in/built-later; proceed with the closest available skill |6667## Examples68### Example 1 — National statistics office, DCAT-AP harvesting69```70/portaljs-architect We're a national statistics office. ~200 datasets, mostly large71CSVs (some GBs), updated quarterly, all public, and we must publish DCAT-AP for the72EU data portal.73```74Infers a multi-publisher, analytics-grade portal. Recommends Parquet on R2 + DuckLake +75DuckDB, static Cloudflare Pages, Frictionless + DCAT-AP metadata, `owner` namespace,76data tier `external` for the Parquet, `DATA_QUERY=duckdb`. Writes `ARCHITECTURE.md` and77hands off to `/portaljs-new-portal` then `/portaljs-add-dataset`.7879### Example 2 — Small nonprofit, no arguments given80```81/portaljs-architect82```83Runs the full four-round interview since nothing was pre-filled. Accepting defaults at84each round lands on the opinionated default stack: repo files or Git-LFS + R2 storage,85`datasets.json` catalog, DuckDB compute, static access on Cloudflare Pages, Frictionless86metadata, `theme` namespace, data tier LFS, `DATA_QUERY=duckdb`.8788### Example 3 — Internal catalog with restricted datasets89```90/portaljs-architect Internal engineering data catalog, single team, dozens of CSVs,91some of it access-controlled to specific roles.92```93The private-data answer in Round 2 flips Access/Hosting to runtime + backend RBAC on94Cloudflare Workers — flagged as the larger, opt-in build — while Storage/Catalog/Compute95still follow the volume-based defaults.9697## Resources98- Full workflow: [`.claude/commands/portaljs-architect.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-architect.md)99- Reference: [`references/reference.md`](references/reference.md)100- Decision framework: [`site/content/docs/architecture/decision-framework.md`](https://github.com/datopian/portaljs/blob/main/site/content/docs/architecture/decision-framework.md)101- Related skills: `/portaljs-new-portal`, `/portaljs-add-dataset`, `/portaljs-connect-ckan`, `/portaljs-define-schema`, `/portaljs-deploy`102103---104105**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `plugins/community/portaljs/skills/portaljs-architect/SKILL.md`