feat(audit): add repeatable migration route reconciliation
This commit is contained in:
6 files changed
+1237
No files matched your search
@@ -0,0 +1,326 @@
|
||||
# Migration audit — accepted baseline and Phase 1 evidence
|
||||
|
||||
Report date: **2026-10-06**, as requested. Application baseline: commit
|
||||
`147b55c586f1a176050551003af98903901833eb` on `faithful-astro-migration`.
|
||||
|
||||
## Evidence dates and interpretation
|
||||
|
||||
- **Inherited accepted findings:** the audit accepted in the migration planning
|
||||
conversation. Its observations span October 5–6, 2026. The temporary JSON/HTML
|
||||
did not retain exact request timestamps; these remain `null` in the durable
|
||||
baseline. File modification times were not substituted for observation times.
|
||||
- **Fresh verified observations:** the shell's UTC clock recorded production
|
||||
requests on **2026-10-07, 00:56:33–00:57:25 UTC**. This differs from the report's
|
||||
requested date and is intentionally not backdated.
|
||||
- **Inferences and uncertainty:** route presence does not establish content,
|
||||
visual, accessibility, or functional equivalence. Unrequested routes remain
|
||||
HTTP-unverified. Sitemap `lastmod` is a discovery hint, not a verified edit date.
|
||||
|
||||
Durable evidence:
|
||||
|
||||
- [Accepted route baseline](route-baseline-2026-10-06.json): compact extraction of
|
||||
the saved inventories and selected HTML metadata/link references; no HTML,
|
||||
article bodies, form payloads, or scripts are committed.
|
||||
- [Current route reconciliation](route-reconciliation-2026-10-07-offline.json):
|
||||
fresh production observations reconciled with generated/local routes. Its
|
||||
final derivation was validated offline; the original production request times
|
||||
and complete request ledger are retained. `mode: offline-reconciliation` does
|
||||
not mean the production observations were invented or newly requested offline.
|
||||
- [Historical migration summary](migration-summary.md): preserved unchanged;
|
||||
its earlier completion claims are not current acceptance evidence.
|
||||
|
||||
The `/tmp` paths recorded in baseline provenance are historical origins only.
|
||||
The tool and offline checks do not depend on those files still existing.
|
||||
|
||||
## Executive summary
|
||||
|
||||
**Inherited finding:** this is a substantial, buildable full-site migration,
|
||||
not a new scaffold. Most page families exist, but route coverage overstates
|
||||
readiness. Rendering, factual content, conversion behavior, and SEO need work.
|
||||
|
||||
**Fresh route verification:** the core inventory is unchanged: 97 generated
|
||||
HTML pages, 102 production sitemap URLs, and 92 overlapping routes. Ten
|
||||
sitemap-listed pages are missing locally. Five generated routes are absent
|
||||
from the production sitemap; four remain live and one is the `/ar` placeholder.
|
||||
|
||||
Phase 1 adds evidence and tooling only. It does not repair rendering, alter
|
||||
content, activate forms, change indexing, or implement redirects.
|
||||
|
||||
## Last known project state
|
||||
|
||||
**Inherited, corroborated by unchanged HEAD:** initial migration work landed in
|
||||
June 2026. The newest local English article publication date was June 1, 2026.
|
||||
Subsequent work concentrated on fidelity, page-specific layouts, FAQ behavior,
|
||||
static inquiry drawers, and authoring infrastructure. Astro upgrades occurred
|
||||
in July. The final August 28 commits enabled Sätteri features, restored Giveback
|
||||
layout, and included MDX routes in sitemap generation.
|
||||
|
||||
Phase 0 fetched both remotes successfully. The migration branch was ahead of
|
||||
`origin/faithful-astro-migration` by 14 commits and its `github` counterpart by
|
||||
16; no remote-only commits were found. Local `main` and the reference foundation
|
||||
branch matched their corresponding remote branches. The pre-existing untracked
|
||||
`merge-plan.md` remains separate, unapproved merger planning, not migration
|
||||
implementation authority.
|
||||
|
||||
## Current architecture
|
||||
|
||||
**Inherited inspection:** Astro 7.1.5, TypeScript, npm, static output, Zod content
|
||||
collections, Markdown/MDX, plain CSS, and self-hosted fonts remain a suitable
|
||||
foundation. English/Spanish/Arabic routing and RTL support exist. Some page copy
|
||||
is rendered from dedicated components rather than its Markdown record.
|
||||
|
||||
The source contains 21 English, nine Spanish, and two Arabic page records;
|
||||
46 English, 12 Spanish, and seven Arabic posts; and one author record per
|
||||
language. Assets comprise 172 image files, 20 font files, and two PDFs.
|
||||
|
||||
**Inherited blocker, deliberately unchanged:** directive parsing removes the
|
||||
`:1` and `:2` portions of ordinary therapy ratios. The accepted audit reproduced
|
||||
this despite a passing build. Phase 2 must inspect intentional directive use
|
||||
before changing the parser and preserve explicit heading IDs.
|
||||
|
||||
## Migration coverage
|
||||
|
||||
| Area | Accepted assessment | Remaining work |
|
||||
| --- | --- | --- |
|
||||
| Service/conversion pages | Implemented, materially stale in places | Ages, eligibility, program naming, CTA destinations, consultation/referral copy |
|
||||
| Homepages | Substantial implementation | English HSA/FSA section and metadata; source-specific Spanish reconciliation |
|
||||
| About, careers, intake, insurance | Sampled substantive copy appeared aligned | Full visual/functional acceptance remains incomplete |
|
||||
| Team | Implemented but stale | Reconcile names, roles, ordering, images |
|
||||
| Employee portal | Effectively an empty page body | Human decision on replacement/access behavior |
|
||||
| Library | 65 local posts; partial index behavior | Nine missing English posts, rewritten articles, search, pagination, authors |
|
||||
| Spanish | Partial page coverage; 12 Library routes | Privacy page, updated source-supported translations, human language review |
|
||||
| Arabic | Seven Library posts plus index and homepage placeholder | RTL/content review; intended unpublished homepage policy not implemented |
|
||||
| SEO | Shared metadata, organization schema, selected FAQs | Publication rules, canonicals, hreflang, article/FAQ schema, social gaps |
|
||||
| Forms/integrations | Static forms; selected external dependencies | Explicit activation and ownership decisions before launch |
|
||||
|
||||
These are inherited content assessments. Phase 1's fresh requests recheck route
|
||||
and selected metadata evidence, not every paragraph or page layout.
|
||||
|
||||
## Route reconciliation
|
||||
|
||||
| Measure | Accepted core count | Fresh result |
|
||||
| --- | ---: | ---: |
|
||||
| Generated HTML pages | 97 | 97 |
|
||||
| Production sitemap URLs | 102 | 102 |
|
||||
| Sitemap/local overlap | 92 | 92 |
|
||||
| Sitemap URLs missing locally | 10 | 10 |
|
||||
| Local generated routes absent from sitemap | 5 | 5 |
|
||||
| Production sitemap additions/removals | — | 0 / 0 |
|
||||
|
||||
The final machine inventory has **129 normalized route keys**, including
|
||||
aliases, utility/archive routes, and the local placeholder. This is not 129
|
||||
verified production pages. `robots.txt` and `sitemap.xml` are recorded separately
|
||||
as two generated utilities, not counted among the 97 HTML pages.
|
||||
|
||||
Ten sitemap-listed missing routes, all freshly verified HTTP 200:
|
||||
|
||||
```text
|
||||
/es/privacy-policy
|
||||
/library/does-aba-therapy-replace-school
|
||||
/library/autism-daily-living-skills-challenges
|
||||
/library/how-many-hours-aba-child-need
|
||||
/library/autism-after-school-meltdowns
|
||||
/library/autism-back-to-school-transition
|
||||
/library/autism-social-skills-by-age
|
||||
/library/sleep-issues-autistic-children
|
||||
/library/halloween-and-autism
|
||||
/library/picky-eating-autism-parent-guide
|
||||
```
|
||||
|
||||
Fresh checks preserve these sitemap-absent local routes as live:
|
||||
|
||||
- `/employee-portal`: HTTP 200.
|
||||
- `/schedule-consultation`: HTTP 200, source `noindex`.
|
||||
- `/library/community-highlight-meet-rula-diab`: HTTP 200, external canonical.
|
||||
- `/library/new-aia-scottsdale-office`: HTTP 200, external canonical.
|
||||
|
||||
`/ar` remains HTTP 404 in production. Locally it is **generated, `draft:false`,
|
||||
and identifiable as placeholder content**. Its intended nonpublication is an
|
||||
accepted future policy, not current implemented behavior.
|
||||
|
||||
All three short aliases remain 301 redirects to their hyphenated service URLs:
|
||||
`/aba`, `/autismevaluations`, and `/learnersocialclub`.
|
||||
|
||||
### Additional discoveries and checking-method differences
|
||||
|
||||
- Six author-pagination links were recovered from archived HTML beyond the
|
||||
earlier narrative: English `/library/author/rula-diab/page/2` through `/6`, and
|
||||
Spanish `/es/library/author/rula-diab/page/2`. They are historical discoveries,
|
||||
not evidence of six new publications. All now return 200 in fresh checks.
|
||||
- English Library pagination pages 3–6, previously only discovered, now return
|
||||
200. Search, the campaign page, three author indexes, and both languages'
|
||||
page-2 listings also remain verified live.
|
||||
- Two links were first inventoried in the fresh pass:
|
||||
`/library/page/1` and `/library/author/rula-diab/page/1`. They were discovered
|
||||
during the final bounded pagination pass and were **not requested**. Do not
|
||||
assume either is absent, a 200, or a redirect.
|
||||
- `/es` returns 301 to `/es/` without redirect following. The original audit
|
||||
followed redirects and recorded the final 200. This is a transport observation
|
||||
difference, **not established evidence that production changed**. The tool
|
||||
records the Location and does not compare unavailable redirect-body metadata
|
||||
as if title/canonical fields had been deleted.
|
||||
|
||||
There are 21 verified HTTP-200 routes outside the sitemap, including the four
|
||||
already represented locally. The route report distinguishes these from 59
|
||||
overlapping sitemap-listed routes not individually HTTP-verified in this pass.
|
||||
|
||||
## Significant content changes preserved from the accepted audit
|
||||
|
||||
These findings remain historical evidence pending page-specific reconciliation:
|
||||
|
||||
1. [ABA](https://www.azinstitute4autism.com/aba-therapy) and
|
||||
[referrals](https://www.azinstitute4autism.com/referrals) differed from local
|
||||
ages/eligibility copy; the individual ABA program described 18 months–8 years.
|
||||
2. English/Spanish [school readiness](https://www.azinstitute4autism.com/library/aba-school-readiness-arizona)
|
||||
changed from academy promotion to a broader guide; the fall-break article
|
||||
also changed materially.
|
||||
3. The [English homepage](https://www.azinstitute4autism.com/) added HSA/FSA
|
||||
information absent locally. That does not itself justify new JSON-LD.
|
||||
4. The [team listing](https://www.azinstitute4autism.com/team) differed from
|
||||
local names; website differences do not prove employment status.
|
||||
5. Spanish privacy content and nine English articles were absent locally.
|
||||
6. `/lp/early-intervention` existed with an API-backed form; its ownership remains
|
||||
a separate decision from full-site migration.
|
||||
|
||||
## SEO / URL risks
|
||||
|
||||
**Inherited findings, not repaired:** syndicated articles self-canonicalize
|
||||
locally despite external production canonicals; hreflang is absent; BlogPosting
|
||||
and standalone FAQPage schema are missing; display H1 is conflated with SEO
|
||||
title; publication exceptions are not modeled; and the separate sitemap
|
||||
generator extracts canonical strings without adequate publication exclusions.
|
||||
|
||||
Global staging noindex currently protects all generated pages. Turning indexing
|
||||
on would not implement the intended `/ar` or `/schedule-consultation` exceptions.
|
||||
Archive/search routes lack implementations or explicit redirect dispositions.
|
||||
Existing nginx rewrites are report artifacts, not deployed routing behavior.
|
||||
|
||||
The route audit does not enforce these future policies or label overlaps as
|
||||
content-aligned. It records observed behavior for subsequent milestones.
|
||||
|
||||
## Forms / analytics / external integrations
|
||||
|
||||
**Inherited findings:** production uses Jotform, HubSpot forms, a campaign API,
|
||||
Google Analytics/consent setup, and Ahrefs analytics. Astro has disabled static
|
||||
forms, configurable counters, payment links, video, and Leaflet/OpenStreetMap.
|
||||
Form and tracking parity is incomplete. No legal compliance conclusion is made.
|
||||
|
||||
Phase 1 used raw GET requests only. It did not execute production JavaScript,
|
||||
submit forms, contact form/counter APIs, unlock the portal, or fetch external
|
||||
canonical publishers. Same-origin GET form actions were recorded as discovery
|
||||
references (for example `/search`); no field values were submitted.
|
||||
|
||||
## Technical debt, sequence, and human gates
|
||||
|
||||
Keep the existing foundation. Resume through individually reviewed milestones:
|
||||
Markdown rendering; route/publication policy; genuine translation relationships;
|
||||
display/social/article/FAQ metadata; high-impact service and homepage facts;
|
||||
Library capability design and content batches; source-supported multilingual
|
||||
work; detailed visual/accessibility QA; and eventual cutover preparation.
|
||||
|
||||
Library search, pagination, and author routes are required capabilities. Inspect
|
||||
the Astro architecture before selecting search technology; do not copy HubSpot
|
||||
implementation details by default. Correct material service facts before
|
||||
extensive pixel-level matching. Substantive translation changes finish ready
|
||||
for human language review, not self-approved fluency.
|
||||
|
||||
Human decisions remain required for employee access, campaign ownership,
|
||||
production form activation, analytics/consent architecture, and conflicting
|
||||
public eligibility claims. None is authorized as implementation by this report.
|
||||
|
||||
## Reproducing the route reconciliation
|
||||
|
||||
Run from `www/`, using the already installed project dependencies:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run test:routes
|
||||
npm run audit:routes
|
||||
npm run audit:routes -- --check
|
||||
```
|
||||
|
||||
- `audit:routes` requires existing build output. It reads source page/post
|
||||
records and generated HTML; discovers through robots.txt, sitemap(s), five
|
||||
locale/index seeds, links, and targeted checks; then writes a new dated JSON.
|
||||
- It uses sequential same-origin GET requests, at least 750 ms between starts
|
||||
(or the applicable robots crawl-delay), 15-second timeouts, ten sitemap and
|
||||
80-request limits. It records redirects without following them and stops
|
||||
network work on 429, 5xx, or a network failure. Incomplete discovery exits
|
||||
nonzero with evidence where available; inability to establish robots policy
|
||||
stops before page discovery.
|
||||
- Known tracking parameters and trailing slashes are normalized. Unknown and
|
||||
semantic query parameters, their ordering, and fragments are preserved.
|
||||
Fragment references share one HTTP route and are not fetched separately.
|
||||
Arbitrary query variants are not automatically crawled.
|
||||
- Only 404/410 establish verified absence. Sitemap omission, redirects, blocked
|
||||
requests, and network failures never do. Locale and canonical come from HTML
|
||||
where actually inspected; unrequested metadata is not invented.
|
||||
- Normalized route rows retain local counterparts, discovery sources/times,
|
||||
request statuses/times, metadata where collected, and a route-level
|
||||
disposition. The request ledger separately dates each discovery document.
|
||||
- Subsequent runs create new files; they do not overwrite the accepted baseline
|
||||
or prior snapshots. Baseline hashes protect the inherited evidence identity.
|
||||
|
||||
Offline validation of this exact snapshot, with no network or writes:
|
||||
|
||||
```sh
|
||||
npm run audit:routes -- --check --evidence reports/route-reconciliation-2026-10-07-offline.json
|
||||
```
|
||||
|
||||
After intentional local changes, rebuild and reconcile against the same saved
|
||||
production evidence without making production requests:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
npm run audit:routes -- --offline --evidence reports/route-reconciliation-2026-10-07-offline.json
|
||||
npm run audit:routes -- --check
|
||||
```
|
||||
|
||||
`--check` defaults to the snapshot with the latest `generatedAt`. It verifies
|
||||
the application source/asset fingerprint and recomputes route rows, totals, and
|
||||
baseline comparisons. It fails on mismatches or saved discovery warnings. It
|
||||
does **not** certify that production has remained unchanged since observation.
|
||||
Build before capture when application files changed: the tool requires `dist`
|
||||
but cannot prove that an arbitrary pre-existing build was produced from the
|
||||
current source. Application fingerprints exclude this audit tooling and reports.
|
||||
|
||||
## Phase 1 validation and remaining uncertainty
|
||||
|
||||
- Focused offline tests cover normalization, meaningful queries/fragments,
|
||||
deduplication, classification, placeholders, redirects, external canonicals,
|
||||
inherited comparisons, and robots handling.
|
||||
- Fresh discovery performed 70 requests: one robots, one sitemap, 68 pages.
|
||||
Responses: 65 HTTP 200, four 301, one 404. No failures or discovery warnings.
|
||||
One additional preflight `robots.txt` GET preceded the tool run and is not
|
||||
counted in its request ledger; no other production requests were made in Phase 1.
|
||||
- The sitemap membership and ten missing content routes match the accepted
|
||||
baseline. Known gaps remain findings; they do not make a consistent inventory
|
||||
fail its offline check.
|
||||
- Per-page content/layout parity, full accessibility testing, linguistic review,
|
||||
external integrations, and the two newly discovered `/page/1` behaviors remain
|
||||
outside this milestone's verification.
|
||||
- `npm run test:routes`: **9/9 passed**, entirely offline.
|
||||
- `npm run audit:routes`: **passed**, with the fresh observations above.
|
||||
- `npm run audit:routes -- --offline --evidence ...`: **passed**, retaining the
|
||||
same production observations while validating final comparison logic. The
|
||||
superseded preliminary generated artifact was not retained in the repository.
|
||||
- `npm run audit:routes -- --check`: **passed** against the final saved snapshot,
|
||||
including after the final build; no network requests or file writes.
|
||||
- `npm run build`: **passed**, 97 pages, zero errors/warnings/hints.
|
||||
- `npm run audit:links`: **passed**, zero broken internal links; the existing
|
||||
broken-links report remained byte-for-byte unchanged.
|
||||
- `git diff --check`: **passed**. Application source, assets, Astro configuration,
|
||||
dependency declarations, lockfile, and existing migration reports are unchanged.
|
||||
The package manifest adds only the two audit/test commands.
|
||||
- All 97 generated pages retain the saved baseline titles, descriptions,
|
||||
canonicals, headings, JSON-LD, and main-content text after normalizing existing
|
||||
randomly generated contact-obfuscation IDs. Whole-output hashes differ because
|
||||
`ContactObfuscation.astro` already uses `Math.random()` for those IDs; byte
|
||||
identity is not claimed. No application behavior change was introduced.
|
||||
- `merge-plan.md` remains untracked and unchanged. Before/after SHA-256:
|
||||
`015db80cdbaf7d68799265d2070db760155c342b432ce6795f17ebc2218c641c`.
|
||||
|
||||
**Recommendation:** Phase 2 is the next useful milestone, subject to approval.
|
||||
The ratio defect remains unchanged. Search the complete Markdown/MDX corpus and
|
||||
directive consumers before altering parsing; retain explicit heading IDs and
|
||||
add rendered Markdown/MDX regression coverage. No Phase 2 work was performed.
|
||||
Reference in new issue
Block a user