Heratio Help Center article. Category: System Administration.
Heratio Standalone Install — Plan
Status: Planning document. No implementation yet. Scope is "what must exist before a fresh Linux box can become a working Heratio without touching AtoM."
Author: Johan Pieterse / Plain Sailing Information Systems Last reviewed: 2026-04-17
1. The problem
Heratio lives in /usr/share/nginx/heratio and runs against a MySQL database (heratio) whose schema and core data were inherited from AtoM. The tables information_object, actor, term, taxonomy, setting, menu, and ~200 others come from AtoM's Propel ORM; the seed data in those tables was loaded by AtoM's YAML fixture loader; the 91 AHG plugins living under /usr/share/nginx/archive/atom-ahg-plugins/ each contributed their own install.sql.
When Heratio is deployed to a customer who is not running AtoM, none of that exists. A fresh CREATE DATABASE heratio will not produce a working Heratio — it will produce a system that crashes on the first page load because taxonomy.id = 30 (QubitTaxonomy root) isn't there, ahg_dropdown is empty, setting has no entries, etc.
This plan defines what a standalone install needs to ship so that one command on a fresh box yields a functional Heratio with:
- All tables created (core Qubit + all AHG plugin tables + all Heratio-specific tables).
- All static seed data loaded (taxonomies, terms, dropdowns, settings, ACL groups, menus, static pages, eras).
- All derived data bootstrapped (Elasticsearch indices, storage paths, default admin user).
The AtoM codebase itself is not a runtime dependency. Heratio is Laravel; AtoM is Symfony/Propel. We only need AtoM's seed artifacts, ported into Heratio's install surface.
2. Current state — what we're starting from
2.1 Heratio side
packages/— 88 Laravel packages. Each has aServiceProvider, views, routes, controllers.packages/ahg-research/database/install.sql— the only install.sql in Heratio today.packages/ahg-research/database/seed_dropdowns.sql— dropdown values for the research module.packages/ahg-ric/database/seed_ric_from_existing.sql— RiC sync from an existing DB.packages/ahg-settings/database/seed_help_settings.sql— help article settings.bin/release— version-bump + push. Not relevant to install.- Package
ServiceProvider::boot()methods auto-seed dropdowns on first boot if entries are missing — this is the existing safety net. config/heratio.php— storage path config; no hardcoded paths elsewhere.
Running DB on this server has 974 tables and 3,824 ahg_dropdown rows. That's the reference we need to reproduce.
2.2 AtoM side (read-only reference)
-
/usr/share/nginx/archive/data/sql/lib.model.schema.sql— 1,467 lines. Core Qubit schema (information_object, actor, term, taxonomy, slug, object, etc.). -
/usr/share/nginx/archive/data/sql/plugins.qbAclPlugin.lib.model.schema.sql— 114 lines. ACL tables. -
/usr/share/nginx/archive/data/sql/plugins.qtAccessionPlugin.lib.model.schema.sql— 203 lines. Accession tables. -
/usr/share/nginx/archive/data/fixtures/*.yml— 6 YAML fixture files (~17,400 lines total):File Lines Content taxonomyTerms.yml13,014 All Qubit taxonomies + terms, multilingual menus.yml2,402 Admin + public navigation menus settings.yml1,586 System settings defaults acl.yml309 ACL groups, permissions, roles staticPages.yml101 Home, About, Contact, Privacy, Terms fixtures.yml17 Small bootstrap — admin user, etc. -
/usr/share/nginx/archive/atom-framework/database/install.sql— AtoM framework tables (access_audit_log, access_request, heritage_*, etc.) — a consolidated schema added by AHG. -
/usr/share/nginx/archive/atom-framework/database/migrations/*.sql— 10 migration SQL files layered on the framework. -
/usr/share/nginx/archive/atom-framework/data/eras/periodo-eras.sql— PeriodO historical-eras seed. -
/usr/share/nginx/archive/atom-framework/database/seeders/ExtendedRightsSeeder.php— PHP seeder for rights taxonomy. -
/usr/share/nginx/archive/atom-ahg-plugins/*/database/install.sql— 81 plugin install.sql files, one per AHG plugin. -
/usr/share/nginx/archive/atom-ahg-plugins/ahgReportBuilderPlugin/database/seed_templates.sql— system report templates. -
/usr/share/nginx/archive/atom-ahg-plugins/ahgHeritagePlugin/data/graph_seed.sql— heritage graph demo data (demo only — skip for production standalone install).
2.3 The gap
- Heratio currently has 1 install.sql. AtoM has 81 + 2 core = 83 schema files producing the tables Heratio relies on. All 83 need to be consolidated into Heratio's install surface.
- The 6 YAML fixtures are only loadable by AtoM's Symfony CLI (
php symfony propel:data-load). They must be converted to raw SQL INSERTs once so Heratio can load them without a Symfony runtime. - Heratio's
ahg_dropdowntable (3,824 rows) is a newer mechanism that replaces ENUMs — already seeded per-package viaServiceProvider::boot(). That stays. docs/x7b-psis-install-sql-worklist.mdalready tracks 50 Cat-B tables across 22 plugins that Heratio code references but no plugin provisions. Those need to be merged into the source-of-truth install.sql files first, then consolidated.
3. The target end state
A fresh Ubuntu 22/24 LTS box with MySQL 8, PHP 8.3, Nginx, Elasticsearch 7/8, Redis, Composer, Node, and git, given only:
git clone git@github.com:ArchiveHeritageGroup/heratio.git /usr/share/nginx/heratio
cd /usr/share/nginx/heratio
./bin/install --domain=mysite.example --admin-email=me@mysite.example
…ends up with:
heratioMySQL database created and populated (~970 tables, ~4,000 dropdown rows, taxonomies and terms loaded, default settings in place, admin user created with a printed-once password).- Elasticsearch indices
heratio_qubitinformationobject,heratio_qubitactor,heratio_qubitterm,heratio_qubitrepositorycreated and reindexed from the empty DB. - Storage paths created under the configured
HERATIO_STORAGE_PATHroot. - Nginx vhost written, PHP-FPM wired, app URL resolves.
/admin/loginworks./admin/dashboardsloads./admin/dropdownsshows values./informationobject/browseloads an empty GLAM grid.- Zero references to AtoM at runtime. AtoM codebase not present, not required.
Cantaloupe (IIIF), Fuseki (RiC), Qdrant (semantic search), Ollama (AI) are optional services — install walks the admin through each but does not block install.
4. Strategy
Three approaches were considered:
| Option | Description | Pros | Cons |
|---|---|---|---|
| A — Dump-and-ship | mysqldump heratio --no-data for schema + mysqldump heratio for seed rows on ~20 reference tables, commit the .sql to the repo |
Fastest to produce; reflects exact production reality | Schema + data become opaque binary-ish blobs; drift is invisible; no per-plugin ownership; hard to diff in PR review |
| B — Compose from sources | Port the 83 AtoM/AHG schema files and 6 YAML fixtures into Heratio's packages/*/database/ folders, one package owns one install.sql |
Per-package ownership; readable diffs; aligns with existing Heratio ServiceProvider auto-seed pattern; clean break from AtoM tree |
More one-time work; YAML→SQL conversion needs a script |
| C — Hybrid (recommended) | Do B as canonical; auto-generate A in CI as a release artifact for fast install + verification | Both benefits; B stays the source of truth, A is a cached build output | Requires install-verification CI job |
Recommendation: C. Own the schema/seeds per-package (option B). Treat the monolithic dump as a build artifact, not source.
5. The install surface (what ships with Heratio)
After this plan is executed, the install surface is:
/usr/share/nginx/heratio/
├── bin/
│ └── install # NEW — bootstrap script (see §6)
├── database/
│ ├── core/
│ │ ├── 00_core_schema.sql # NEW — ported lib.model.schema.sql
│ │ ├── 01_acl_schema.sql # NEW — ported qbAclPlugin schema
│ │ ├── 02_accession_schema.sql # NEW — ported qtAccessionPlugin schema
│ │ └── 03_framework.sql # NEW — ported atom-framework/install.sql
│ ├── seeds/
│ │ ├── 00_taxonomies.sql # NEW — YAML→SQL from taxonomyTerms.yml
│ │ ├── 01_settings.sql # NEW — YAML→SQL from settings.yml
│ │ ├── 02_menus.sql # NEW — YAML→SQL from menus.yml
│ │ ├── 03_acl.sql # NEW — YAML→SQL from acl.yml
│ │ ├── 04_static_pages.sql # NEW — YAML→SQL from staticPages.yml
│ │ ├── 05_bootstrap.sql # NEW — YAML→SQL from fixtures.yml
│ │ ├── 06_eras.sql # NEW — copied from periodo-eras.sql
│ │ └── 07_report_templates.sql # NEW — copied from ahgReportBuilderPlugin
│ └── build/
│ └── heratio-full-install.sql # NEW — auto-generated catenation (option A dump)
├── packages/
│ └── ahg-*/database/
│ ├── install.sql # NEW — ported from atom-ahg-plugins/<plugin>/database/install.sql
│ └── seed_dropdowns.sql # EXISTING pattern, extended where needed
└── docs/
├── standalone-install-plan.md # THIS DOC
├── standalone-install-howto.md # NEW — admin-facing install guide
└── x7b-psis-install-sql-worklist.md # EXISTING — feeds into §7
The convention:
- Core schema and global fixtures live in
database/core/anddatabase/seeds/at the repo root. They are not owned by any one package because they underpin everything. - Plugin-specific schema lives in
packages/<plugin>/database/install.sql. Each Laravel package already has aServiceProvider— that provider becomes responsible for running its own install.sql on first boot (following the existingahg-researchpattern). - Dropdown seeds continue to use the existing per-package
seed_dropdowns.sql+ServiceProvider::boot()auto-seed mechanism. No change needed — that's already the Heratio standard.
6. The bin/install script
Single entry point for a fresh install. Idempotent — safe to re-run.
bin/install [--domain=<host>] [--admin-email=<email>] [--admin-password=<pw>]
[--storage-path=<path>] [--skip-es] [--skip-nginx]
[--non-interactive]
Stages, in order:
- Preflight — check PHP 8.3, MySQL 8, Elasticsearch 7+, Redis, Node 18+, Composer,
git. Abort with a checklist on miss. - Composer install —
composer install --no-devin project root. - NPM build —
npm ci && npm run buildfor bundled assets. - .env bootstrap — copy
.env.example→.env, fillAPP_URL,DB_*,ELASTICSEARCH_*,HERATIO_*from flags/prompts, runphp artisan key:generate. - Database create —
CREATE DATABASE heratio CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci. - Core schema — load
database/core/*.sqlin name order. 4 files, ~2,000 lines. - Plugin schemas — load every
packages/*/database/install.sqlin alphabetical order. 88 files target. - Global seeds — load
database/seeds/*.sqlin name order. 8 files. - Laravel boot sweep —
php artisan heratio:install-bootstrap(new command) — fires everyServiceProvider::boot()so the auto-seed dropdowns run. - Admin user — create a single admin with the flag-provided or randomly generated password; print it once.
- Storage — create
HERATIO_STORAGE_PATH,HERATIO_UPLOADS_PATH,HERATIO_BACKUPS_PATHwith correct permissions. - Elasticsearch —
php artisan ahg:es-reindex --dropto create and populate indices. - Nginx (unless
--skip-nginx) — write/etc/nginx/sites-available/heratio.conffrom a template, symlink,nginx -t && systemctl reload nginx. - Smoke test —
curl -I https://<domain>/expect 200 or redirect;curl -sS /admin/dropdownsexpect HTML with "Dropdown Manager";curl -sS /informationobject/browseexpect 200. - Report — print URL, admin email+password, ES index status, storage paths, Cantaloupe/Fuseki/Qdrant/Ollama opt-in hints.
Crash behaviour: every stage wraps in set -e. Failure at any stage prints the failing stage and exits non-zero. Re-running skips already-done stages (detects via schema presence checks).
7. Work items — what has to be built
Ordered by dependency. Each item is ~small, scoped, independently releasable.
Phase 1 — Consolidate schema sources (blocking)
- Port core AtoM schema — Copy
data/sql/lib.model.schema.sql+qbAclPlugin+qtAccessionPluginfrom AtoM intodatabase/core/00..02_*.sql. Find-replace anyCREATE TABLE <name>→CREATE TABLE IF NOT EXISTS <name>. Verify bymysql -e "SOURCE database/core/00_core_schema.sql"on an empty DB. - Port AtoM framework schema — Copy
atom-framework/database/install.sqlintodatabase/core/03_framework.sql. Apply the 10 migration files fromatom-framework/database/migrations/*.sqlinline (so the result is one consolidatedCREATE TABLE IF NOT EXISTSfile with everything the migrations would have added). - Port each of the 81 AHG plugin install.sql into its matching Heratio package:
atom-ahg-plugins/ahgCorePlugin/database/install.sql→packages/ahg-core/database/install.sqlatom-ahg-plugins/ahgSettingsPlugin/database/install.sql→packages/ahg-settings/database/install.sql- …one per package. Mapping table in §9 below.
- Resolve the x7b worklist — the 50 Cat-B tables in
docs/x7b-psis-install-sql-worklist.mdneed to land in their owning package install.sql files. Already has DDL drafted — just needs placement. - Add
CREATE TABLE IF NOT EXISTSguard to every ported file so re-running is safe. - Update each package's
ServiceProvider::boot()to invoke itsinstall.sqlon first boot (followahg-researchpattern — check if a sentinel table exists, if not run the SQL).
Phase 2 — Convert AtoM YAML fixtures to SQL
- Write
tools/atom-fixture-to-sql.php— one-shot converter. Input: one of the 6 YAML files. Output: an SQL file ofINSERT IGNOREstatements. Must handle:- Nested
i18narrays → multiple rows in*_i18ntables (one per culture). - Symbolic IDs (
QubitTaxonomy_root,QubitAclGroup_anonymous) → resolve to numeric IDs via a first pass building a symbol-table. source_culturefield → carry through as-is.- Parent references (
parent_id: QubitTaxonomy_root) → resolve via symbol table.
- Nested
- Run the converter once per YAML file. Commit the resulting SQL to
database/seeds/. The converter itself is a one-off and lives undertools/(not shipped at runtime). - Verification: load the generated SQL into a fresh DB, compare
SELECT COUNT(*) FROM taxonomy,term,setting,acl_group,menu,static_pageagainst a production Heratio DB. Counts must match.
Phase 3 — bin/install script
- Write
bin/installin bash. Stages per §6. - Write
php artisan heratio:install-bootstrap— fires every ServiceProvider's install + seed. Replaces the first-visit auto-seed for the install-time bulk load. - Write
.env.examplewith everyHERATIO_*,RIC_*,ELASTICSEARCH_*key referenced in the codebase, with sensible defaults and inline comments. - Write
config/nginx/heratio.conf.templatewith placeholders for domain + doc_root. Apply during install.
Phase 4 — Build artifact (option A side of the hybrid)
- GitHub Actions workflow
build-install-artifact.yml:- Spin up MySQL 8.
- Run
bin/install --non-interactiveagainst it. mysqldump heratio --no-data --routines --triggers→database/build/heratio-schema.sql.mysqldump heratio <seed-tables-only>→database/build/heratio-seeds.sql.- Publish both as release artifacts.
- Document in
standalone-install-howto.mdthat impatient admins can load these two dumps instead of runningbin/install.
Phase 5 — Admin-facing documentation
- Write
docs/standalone-install-howto.md— the user-facing install guide. System requirements, thebin/installcommand, post-install steps for each optional service (Cantaloupe, Fuseki, Qdrant, Ollama), troubleshooting. - Ingest it into the help system via
php artisan ahg:help-ingest.
8. Open questions / decisions needed
- Q1: Which taxonomies are "core" vs. AHG-extension? The fixture
taxonomyTerms.ymlcontains both AtoM-vanilla taxonomies (description level, media type, ISO languages) and AHG-added ones (heritage sectors, POPIA categories). For an international install, should we ship both, or split into "core" (Heratio ships) + "optional SA" (customer opts in)? — Matches the existing "pluggable per-market" positioning. - Q2: What is the minimum ES index state? Do we ship empty indices, or seeded with a "welcome record"? — Recommend empty. Admin can ingest their own.
- Q3: License + attribution. AtoM is AGPL-3.0. Heratio is AGPL-3.0. Ported YAMLs and schema SQLs retain upstream copyright attribution in the file header. Confirm this is acceptable before the port begins.
- Q4: Cantaloupe/Fuseki/Qdrant/Ollama — ship installers? Option:
bin/install --with-cantaloupedownloads and configures. Option: bin/install only prints instructions and the admin runs a separatebin/install-cantaloupelater. Recommend: separate sub-installers, simpler to maintain. - Q5: Upgrade path. Existing sites running Heratio against an AtoM-seeded DB need a no-op upgrade — the new
bin/installmust detect "DB already populated" and skip §6.5–§6.8. Yes. Idempotence is a hard requirement. - Q6: Sample data. Should standalone install offer
--with-demo-datathat loads a few fonds/actors/digital objects so admins can see the system work immediately? — Recommend: yes, small curated set (~20 records, 1 fonds, 3 actors, 5 digital objects). Ships asdatabase/seeds/99_demo_data.sql, opt-in only.
9. Plugin mapping (AtoM → Heratio)
Required for Phase 1 step 3. Below is the mapping between the 91 AtoM AHG plugins and the 88 Heratio packages. Names differ in case convention (camelCase → kebab-case).
Generated mechanically — each row is "AtoM plugin name" → "Heratio package name". Validate before bulk port.
| AtoM plugin | Heratio package |
|---|---|
| ahg3DModelPlugin | ahg-3d-model |
| ahgAccessionManagePlugin | ahg-accession-manage |
| ahgAccessRequestPlugin | ahg-access-request |
| ahgActorManagePlugin | ahg-actor-manage |
| ahgAIPlugin | ahg-ai-services |
| ahgAiConditionPlugin | ahg-ai-condition |
| ahgAPIPlugin | ahg-api |
| ahgAuditTrailPlugin | ahg-audit-trail |
| ahgAuthorityPlugin | ahg-authority |
| ahgBackupPlugin | ahg-backup |
| ahgCartPlugin | ahg-cart |
| ahgCDPAPlugin | ahg-cdpa |
| ahgConditionPlugin | ahg-condition |
| ahgContactPlugin | ahg-contact |
| ahgCorePlugin | ahg-core |
| ahgCustomFieldsPlugin | ahg-custom-fields |
| ahgDacsManagePlugin | ahg-dacs-manage |
| ahgDAMPlugin | ahg-dam |
| ahgDataMigrationPlugin | ahg-data-migration |
| ahgDcManagePlugin | ahg-dc-manage |
| ahgDedupePlugin | ahg-dedupe |
| ahgDiscoveryPlugin | ahg-discovery |
| ahgDisplayPlugin | ahg-display |
| ahgDoiPlugin | ahg-doi |
| ahgDonorAgreementPlugin | ahg-donor-agreement |
| ahgDonorManagePlugin | ahg-donor-manage |
| ahgExhibitionPlugin | ahg-exhibition |
| ahgExportPlugin | ahg-export |
| ahgExtendedRightsPlugin | ahg-extended-rights |
| ahgFavoritesPlugin | ahg-favorites |
| ahgFederationPlugin | ahg-federation |
| ahgFeedbackPlugin | ahg-feedback |
| ahgFormsPlugin | ahg-forms |
| ahgFtpPlugin | ahg-ftp |
| ahgFunctionManagePlugin | ahg-function-manage |
| ahgGalleryPlugin | ahg-gallery |
| ahgGISPlugin | ahg-gis |
| ahgGraphQLPlugin | ahg-graphql |
| ahgHelpPlugin | ahg-help |
| ahgHeritageAccountingPlugin | ahg-heritage-accounting |
| ahgHeritagePlugin | ahg-heritage-manage |
| ahgICIPPlugin | ahg-icip |
| ahgIiifPlugin | ahg-iiif |
| ahgInformationObjectManagePlugin | ahg-information-object-manage |
| ahgIngestPlugin | ahg-ingest |
| ahgIntegrityPlugin | ahg-integrity |
| ahgIPSASPlugin | ahg-ipsas |
| ahgJobsManagePlugin | ahg-jobs-manage |
| ahgLabelPlugin | ahg-label |
| ahgLandingPagePlugin | ahg-landing-page |
| ahgLibraryPlugin | ahg-library |
| ahgLoanPlugin | ahg-loan |
| ahgMarketplacePlugin | ahg-marketplace |
| ahgMenuManagePlugin | ahg-menu-manage |
| ahgMetadataExportPlugin | ahg-metadata-export |
| ahgMetadataExtractionPlugin | ahg-metadata-extraction |
| ahgModsManagePlugin | ahg-mods-manage |
| ahgMultiTenantPlugin | ahg-multi-tenant |
| ahgMuseumPlugin | ahg-museum |
| ahgNAZPlugin | ahg-naz |
| ahgNMMZPlugin | ahg-nmmz |
| ahgPortableExportPlugin | ahg-portable-export |
| ahgPreservationPlugin | ahg-preservation |
| ahgPrivacyPlugin | ahg-privacy |
| ahgProvenancePlugin | ahg-provenance |
| ahgRadManagePlugin | ahg-rad-manage |
| ahgRegistryPlugin | ahg-registry |
| ahgReportBuilderPlugin | ahg-report-builder |
| ahgReportsPlugin | ahg-reports |
| ahgRepositoryManagePlugin | ahg-repository-manage |
| ahgRequestToPublishPlugin | ahg-request-to-publish |
| ahgResearcherPlugin | ahg-researcher |
| ahgResearchPlugin | ahg-research |
| ahgRicExplorerPlugin | ahg-ric |
| ahgRightsHolderManagePlugin | ahg-rights-holder-manage |
| ahgRightsPlugin | ahg-rights |
| ahgSearchPlugin | ahg-search |
| ahgSecurityClearancePlugin | ahg-security-clearance |
| ahgSemanticSearchPlugin | ahg-semantic-search |
| ahgSettingsPlugin | ahg-settings |
| ahgSpectrumPlugin | ahg-spectrum |
| ahgStaticPagePlugin | ahg-static-page |
| ahgStatisticsPlugin | ahg-statistics |
| ahgStorageManagePlugin | ahg-storage-manage |
| ahgTermTaxonomyPlugin | ahg-term-taxonomy |
| ahgThemeB5Plugin | ahg-theme-b5 |
| ahgTiffPdfMergePlugin | ahg-tiff-pdf-merge |
| ahgTranslationPlugin | ahg-translation |
| ahgUiOverridesPlugin | ahg-ui-overrides |
| ahgUserManagePlugin | ahg-user-manage |
| ahgUserRegistrationPlugin | ahg-user-registration |
| ahgVendorPlugin | ahg-vendor |
| ahgWorkflowPlugin | ahg-workflow |
3 AtoM plugins with no direct Heratio counterpart — decide per plugin whether to fold into an existing Heratio package or create a new one:
ahgMetadataExtractionPlugin— likely folds intoahg-ingestorahg-ai-services.ahgLoanPlugin— likely its own newahg-loanpackage.ahgRequestToPublishPlugin— likely folds intoahg-workfloworahg-access-request.
10. Time and effort estimate
| Phase | Items | Estimate |
|---|---|---|
| Phase 1 (schema consolidation) | 6 items | 2–3 days — most of it is mechanical copy-with-guards + running against a scratch DB to confirm CREATE TABLE IF NOT EXISTS guards catch everything. |
| Phase 2 (YAML → SQL converter) | 3 items | 2 days — the symbol-table pass is the tricky part; everything else is straightforward. |
| Phase 3 (bin/install + bootstrap command) | 4 items | 1–2 days. |
| Phase 4 (build artifact + CI) | 2 items | 0.5 day. |
| Phase 5 (documentation + help ingest) | 2 items | 0.5 day. |
| Total | 17 items | ~1 week of focused work |
11. Out of scope for this plan
- Data migration from an existing AtoM install to a fresh Heratio — that's a separate doc (
docs/data-migration-user-guide.mdalready exists; cross-link it from the install doc). - Multi-tenant install — single-tenant is the target here. Multi-tenant uses the same install but adds tenant bootstrapping via
ahg-multi-tenant. - Kubernetes / Docker deployment — bare-metal / VM install first. Docker image build is a future task, trivial on top of
bin/install --non-interactive. - Backup / restore — Heratio already has
ahg:backupand/admin/restore. Install doesn't touch those.
12. Success criteria
Install is "done" when:
- A fresh Ubuntu 24 LTS box with only MySQL, PHP, Nginx, Redis, Elasticsearch installed can become a running Heratio with one
bin/installinvocation and no manual DB work. - The resulting DB has ≥ 97% of the table count of the current production heratio DB (allowing for demo-data-only tables).
/admin/dropdownsshows ≥ 3,800 rows without the auto-seed having to run./admin/loginworks with the printed credentials./informationobject/browserenders the empty GLAM grid at HTTP 200 with no PHP notices.php artisan ahg:es-reindex --dropcompletes with no errors against the empty DB.- CI job
build-install-artifactsucceeds against a fresh MySQL 8 container and publishesheratio-schema.sql+heratio-seeds.sqlrelease artifacts.