portaljs-architect

$npx mdskill add datopian/portaljs/portaljs-architect

Recommends a data-portal architecture from stated needs and hands off to build skills.

  • Solves the problem of choosing an architecture for a new data portal project.
  • Depends on Read, Write, Bash, and user interview for requirements.
  • Decides by interviewing the user and filling six architecture slots with defaults.
  • Delivers a structured architecture recommendation and triggers the appropriate build skills.

SKILL.md

.github/skills/portaljs-architectView on GitHub ↗
---
name: portaljs-architect
description: 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.
allowed-tools: Read, Write, Bash(du:*), Bash(wc:*), Bash(ls:*), Bash(head:*)
version: 1.0.0
author: Datopian <[email protected]>
license: MIT
compatibility: Claude Code with PortalJS portals (Next.js 14, React 18, Node 18+). Runs from any project via the plugin, a personal ~/.claude/commands install, or a portaljs clone.
tags:
  - portaljs
  - data-portal
  - architecture
  - advisory
  - ckan
  - nextjs
---

# 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`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-architect.md) —
that file is the single source of truth. Follow it when executing this skill:

1. 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.
2. 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`).
3. Echo the architecture brief (stack, reasoning per slot, deviations from default,
   deferred items) and wait for confirmation ("go") or corrections.
4. On confirmation, persist the brief to `./ARCHITECTURE.md` in the working directory.
5. 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
- Full workflow: [`.claude/commands/portaljs-architect.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-architect.md)
- Reference: [`references/reference.md`](references/reference.md)
- Decision framework: [`site/content/docs/architecture/decision-framework.md`](https://github.com/datopian/portaljs/blob/main/site/content/docs/architecture/decision-framework.md)
- Related skills: `/portaljs-new-portal`, `/portaljs-add-dataset`, `/portaljs-connect-ckan`, `/portaljs-define-schema`, `/portaljs-deploy`

More from datopian/portaljs

SkillDescription
arcgis-to-portaljsMigrate a whole ArcGIS Hub site into a PortalJS Arc portal end-to-end. Harvests the Hub /data.json (DCAT-US) inventory, exports every FeatureService layer through the ArcGIS REST query API with resultOffset paging, converts each to the serverless dual tier (PMTiles render + GeoParquet query) with tabular items to Parquet, pushes everything to Cloudflare R2 via Git LFS, appends dual-tier datasets.json entries, and writes a source-vs-derived parity report. Use to move a City or sector ArcGIS Hub open-data portal onto PortalJS with no server-side compute.
portaljs-add-chartAdd a chart (line, bar, area, pie, or scatter) to a dataset's showcase in a PortalJS portal. Installs recharts, writes a reusable Chart component, and renders it in the showcase Views section. Use when visualizing a dataset already registered in datasets.json.
portaljs-add-datasetAdd a dataset (CSV, TSV, JSON, or GeoJSON) to an existing PortalJS portal. Appends an entry to datasets.json so the catalog and showcase render it automatically; routes the data by source (local file vs remote URL) — R2 via Git LFS by default, remote URLs by passthrough. Use when registering a new dataset in a scaffolded portal.
portaljs-add-dcatMake a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking. Use when a portal needs to be harvested by data.europa.eu, data.gov, or a national open-data catalog.
portaljs-add-geoAuto-ingest a geospatial file (GeoJSON, Shapefile, GeoPackage, KML/KMZ, FlatGeobuf, CSV-with-geometry) into a PortalJS portal on the user's own machine, with no server. Normalizes CRS to EPSG:4326, derives a PMTiles render tier and a GeoParquet query tier, pushes all three artifacts to Cloudflare R2 via Git LFS, and appends one dual-tier datasets.json entry the showcase auto-renders. Use when the source is a vector geo format that needs a map or spatial-query view.
portaljs-add-mapRender a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a Map component, then renders the map for the chosen dataset. Use when a dataset's data is geographic and a map view is needed alongside the showcase's default metadata and download.
portaljs-add-resourceAdd another file (resource) to an EXISTING dataset in a PortalJS portal — a data dictionary, methodology, or an additional data file. Turns a single-file dataset into a multi-resource one; the showcase renders a section per resource. Use when a dataset needs a second file, such as a data dictionary, methodology doc, or an additional period's data.
portaljs-check-data-qualityAudit a local or remote tabular file (CSV/TSV) for common data quality issues — schema, nulls, types, duplicates. Read-only. Use when a dataset needs a quality check before publishing, or a showcase renders wrong (blank cells, garbled numbers, an unsortable date column) and the cause needs isolating.
portaljs-connect-ckanWire a scaffolded PortalJS portal to a CKAN backend over its API. Generates a tiny server-side fetch client (no runtime dependency) and feeds the /search catalog and /@namespace/slug showcases from CKAN instead of datasets.json. Use when connecting an existing portal to a live CKAN instance instead of a static manifest.
portaljs-define-schemaDefine a dataset's metadata profile — infer a Frictionless Table Schema from its data, add Data Package metadata (license, sources, keywords), and write it into datasets.json so the showcase renders a typed field table. Extend or customize via the L0-L3 profile ladder. Use when a registered dataset needs field types, constraints, or catalog metadata before publishing.