Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ Notable user-visible changes to Food Help are recorded here. Community publicati

### Added

- A separate Food Help project homepage build, with an authoritative available-community list, prominent Kingston link, public project/contact context, root sitemap and robots files, and a real 404 page.
- Independent `project:check`, `project:build`, `project:preview` and `project:release` workflows for the root without advancing community releases.
- Top-level Emergency food and Affordable food browsing, with Emergency as the default and a stable `/affordable-food/` route.
- Location-specific resource cards that can share one provider identity without merging venue schedules.
- Isolated local review builds for visibly labelled draft records.

### Changed

- Configured community directories can link discreetly back to the Food Help project without changing their resource URLs or provider data.
- Category controls now show only primary types represented in the active browsing view, and omit “All types” when only one type is available.
- Published resources require reviewed, evidence-backed cost information. Free services appear in Emergency food, low-cost and subsidized services appear in Affordable food, and mixed-cost services appear in both without duplicate records.
- Static, printable, downloaded, offline and public-data surfaces continue to preserve the complete published directory.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Read `AGENTS.md` and the selected `deployments/<community>/AGENTS.md`. Changes to platform behaviour belong in generic source; community facts and choices belong in that deployment folder. Never solve a community requirement with a locality branch in application code.

Use the supported Node version, `npm ci`, and install Playwright browsers. Run `npm run check -- --site deployments/<community>` for a local change or `npm run check -- --all` for shared software before requesting review. Explain the problem, resulting behaviour, verification and any effects on public data, privacy, caches or deployment. Keep dependencies minimal; development-only tools still need a reason and a lockfile change. Validation does not deploy; publishing selects one community and an exact source revision.
Use the supported Node version, `npm ci`, and install Playwright browsers. Run `npm run project:check` for a root project-site change, `npm run check -- --site deployments/<community>` for a local directory change, or `npm run check -- --all` for shared software before requesting review. Explain the problem, resulting behaviour, verification and any effects on public data, privacy, caches or deployment. Keep dependencies minimal; development-only tools still need a reason and a lockfile change. Validation does not deploy; publishing selects the project site or one community and an exact source revision.

Provider suggestions are review leads. Supply source links, what was checked and when, uncertainties and the proposed fact change. Do not submit private correspondence, personal contact details or credentials. A maintainer/operator must approve facts before publication. Automated checks never make that editorial decision.

Expand Down
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,21 @@

Food Help builds an accessible, installable, offline-capable food-resource directory from reviewed local data. It is a static Vite + TypeScript application with no runtime package dependencies, accounts, database or server application.

The project homepage at [food-help.ca](https://food-help.ca) is a separate static target and community hub. Its public copy lives in `project-site/site.json`; `project-site/communities.json` is the single authoritative list of available directories. The root links people to local directories and does not duplicate their food-provider records.

**A new community requires no changes to generic application source.** Shared software lives on `main`; each community supplies a folder containing `site.json`, `resources.json`, optional `assets/` and optional sanitized `usage.json`. Building and releasing one community does not update another.

The real Kingston configuration and eight reviewed listings are in `deployments/kingston/`, for [kingston.food-help.ca](https://kingston.food-help.ca). The neutral starter in `examples/exampleville/` is fictional and excluded from indexing. It must not be used to find real services.

## Work on the project homepage

```sh
npm run project:check
npm run project:dev
```

Open `http://127.0.0.1:4175`. The normal local build is a noindex preview at `dist/project/`; build it without starting a server with `npm run project:build`, or serve an existing build with `npm run project:preview`. A production build and `release/project` pointer are explicitly separate from every community build and release. See [deployment](docs/deployment.md#food-help-project-site) before publication.

## Start independently

Download, clone, fork or use this repository as a template. No TGC account, hosting purchase or analytics service is required. Use Node **24.12 or later in the Node 24 line** (see `.node-version`).
Expand Down Expand Up @@ -59,13 +70,14 @@ Published schedules are never a live availability feed. Location is optional and
| Location | Role |
| --- | --- |
| `deployments/<community>/site.json`, `resources.json` | Community configuration and reviewed fact authority |
| `project-site/site.json`, `communities.json`, `styles.css` | Root project-site copy, available-community authority and presentation |
| Selected folder's `assets/`, optional `usage.json`, `AGENTS.md` | Branding, reviewed aggregates and local rules |
| `examples/exampleville/` | Neutral fictional starter and test fixture |
| `src/`, `schemas/`, `scripts/` | Shared application, data contracts and tooling |
| `src/copy/en.ts` | Application copy and documented additive language path |
| `tests/` | Unit, contract, browser, offline and synthetic-community checks |
| `.github/workflows/` | Repeatable validation, separate from explicit publication |
| `.generated/`, `artifacts/`, `dist/<deployment_id>/` | Disposable generated output; never hand-maintained authority |
| `.generated/`, `artifacts/`, `dist/project/`, `dist/<deployment_id>/` | Disposable generated output; never hand-maintained authority |

Read [maintenance](docs/maintenance.md) for adding listings, communities and shared upgrades; [architecture](docs/architecture.md) for generation/offline behaviour; [interoperability](docs/interoperability.md) for the preserved v1/HSDS mapping; [verification](docs/verification.md) for automated versus physical-device evidence; and the [changelog](CHANGELOG.md) for unreleased user-visible changes.

Expand Down
3 changes: 2 additions & 1 deletion deployments/kingston/site.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,6 @@
"data_rights": {
"statement": "Resource facts remain subject to their original sources and provider rights. No separate open-data licence is asserted for this deployment."
},
"software_source_url": "https://github.com/True-Good-Craft/Food-Help"
"software_source_url": "https://github.com/True-Good-Craft/Food-Help",
"project_home_url": "https://food-help.ca"
}
4 changes: 4 additions & 0 deletions docs/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Food Help and the fictional starter work with `"analytics": { "enabled": false }`. Disabled deployments contain no collector endpoint or collector CSP permission. Public aggregate files are independent of browser collection and never enable it.

The root project site also explicitly keeps analytics disabled. It has no client JavaScript, collector endpoint, cookie, identifier or consent storage. Its contact action is a direct link to the existing public True Good Craft email address; it does not embed the True Good Craft inquiry form or connect to its private form storage.

Project-site interest must not be counted as Kingston directory activity. The existing Food Help/Lighthouse adapter is deployment-scoped, and no root-site collector contract or origin registration is configured here. Any later root measurement would require a separately reviewed property name, allowed `https://food-help.ca` origin, bounded event contract, public wording, retention/reporting treatment and coordinated Lighthouse configuration outside this repository. Until those approvals and compatibility checks exist, keep `project-site/site.json` set to disabled and do not reuse Kingston constants or reporting.

Collection requires an explicitly reviewed deployment configuration, a production build and the exact configured HTTPS canonical origin. Preview URLs and preview builds suppress it. The emitter runs only on the interactive home directory: resource pages, the simple directory, downloads and other informational pages do not produce page-history events.

## Fixed events and wire formats
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

Food Help produces one static community artifact per selected configuration. There is no tenant router, server API, database, account system or admin portal. `deployments/<community>/` contains that community's authority; `schemas/` and shared semantic validation define the contract. `src/`, `schemas/` and `scripts/` are shared on `main`. Generated files are never authoring authority.

The root project site is a smaller, separate static artifact. `project-site/site.json` owns its public copy and integration choices, while `project-site/communities.json` is the only list used to generate community cards, crawlable links, JSON discovery and machine-readable project guidance. Its output is `dist/project/`; it contains no provider records, client JavaScript, service worker, browser storage or analytics. `scripts/build-project.ts` cannot select or write a community output, and community builds do not read the project-site community list.

Setup requires an explicit `--site` folder. Development, build and preview select the same folder, with `examples/exampleville/` as the neutral default when omitted. `check --all` may discover community folders for validation; publication never discovers targets implicitly. Each community release chooses an exact shared-source revision and can retain its prior artifact while another advances.

The project site similarly advances only through `release/project`. It is not a community release pointer and must be connected to a distinct root hosting project. Building, checking, merging or releasing the root does not advance `release/<community>`; adding a community directory does not automatically publish the root list.

## Build order

`scripts/build.ts` owns the pipeline; `scripts/lib/web.ts` renders the related web/discovery surfaces together.
Expand Down
1 change: 1 addition & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Use `--site deployments/kingston` for Kingston or `--site deployments/<community
| `analytics` | No | Explicit optional collection boundary, described below |
| `public_usage` | No | Enable a local sanitized aggregate file; independent of collection |
| `software_source_url` | Required for distribution | Public covered source location; production must identify the exact built revision, including deployed modifications |
| `project_home_url` | No | HTTPS link from a community footer to its broader project/community hub; does not change the directory canonical origin |

V1 ships a reviewed English copy pack (`language: "en"`, `text_direction: "ltr"`); other language/direction combinations are represented in the schema but rejected by the runtime capability check until a copy pack exists. `locale` is not a claim that resource text has been translated. Resource and organization content can carry their own language. No English-only locality or country types exist.

Expand Down
39 changes: 39 additions & 0 deletions docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,45 @@

The application artifact is the selected `dist/<deployment_id>/` folder. Building is local and does not create a hosting project, push a repository, change DNS or contact Cloudflare. Deploy to a dedicated root HTTPS origin. No Cloudflare runtime service is required by the directory application.

## Food Help project site

The root `https://food-help.ca/` project site is an independent target, not a community directory. Its source is `project-site/`, its output is `dist/project/`, and its release pointer is `release/project`. Build and check it locally with:

```sh
npm run project:check
npm run project:build
npm run project:preview
```

The normal build is a noindex review artifact. Production requires corresponding source for the exact revision:

```sh
npm run project:build -- --production --source-revision <full-commit-sha>
npm run project:release -- --ref <commit-or-tag>
```

After review and separate publication authorization, adding `--publish` to `project:release` advances only `release/project` using the same lease-protected release-plan policy as community releases. A dedicated root hosting project should watch only that branch, run `npm run project:build -- --production`, publish `dist/project`, and provide `CF_PAGES_COMMIT_SHA`. It must not watch `release/kingston`, build a `deployments/` folder, or combine root and community outputs. Retain the previous complete root artifact, source revision and hosting release identifier for rollback.

Before the first release, an authorized operator must create or select that distinct static hosting project, attach only the approved `food-help.ca` hostname, disable host-injected analytics/scripts/HTML rewriting, and verify the generated headers, canonical, 404 and sitemap on the actual origin. Preview and provider-generated aliases must remain noindex. The generated project artifact has no client JavaScript, service worker or analytics integration.

The intended redirect posture is one hop with path and query preserved:

- `http://food-help.ca/*` → `https://food-help.ca/*`;
- `http://www.food-help.ca/*` and `https://www.food-help.ca/*` → `https://food-help.ca/*`.

These redirects require the relevant DNS, certificate and hosting configuration and are not made by the repository build. Inspect current zone/project state before applying them; do not change mail or unrelated TGC records.

### Search Console and sitemap submission

Use one Google Search Console **Domain property** for `food-help.ca` so the root and current/future subdomains are covered for ownership. An authorized account owner must add the property and publish Google's supplied DNS TXT verification record. That external verification is distinct from having valid metadata in the build.

After the root and Kingston production origins are live and verified, submit both sitemaps separately within that property:

- `https://food-help.ca/sitemap.xml`
- `https://kingston.food-help.ca/sitemap.xml`

The root sitemap intentionally contains only root-site pages. Each community retains its own canonical URLs, robots policy and sitemap; do not canonicalize or copy subdomain pages into the root sitemap. Search Console verification and sitemap acceptance still do not guarantee crawling or indexing. Record actual submission responses, inspect representative canonical/indexing reports after discovery, and keep previews, fictional examples and provider aliases noindex.

Before production, review public facts, data rights, contacts, operator statement, optional analytics policy, source availability and canonical origin. Set `example_content: false` only after replacing the fictional dataset. Enable indexing explicitly if appropriate. For Kingston:

```sh
Expand Down
6 changes: 6 additions & 0 deletions docs/maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Configuration and facts belong in the folder selected by `--site`. For Kingston this is `deployments/kingston/`; independent operators can use their own folder. Generic source must not acquire community-specific branches or hardcoded locality values.

## Maintain the project homepage and community list

Root-site copy and integration choices live in `project-site/site.json`. Available community names, coverage and canonical directory URLs live only in `project-site/communities.json`; do not duplicate provider records or add fictional/coming-soon communities. Keep an ordinary HTTPS link from the root to each local directory, and set that directory's optional `project_home_url` when it should link back.

Adding a community requires two separately reviewed publication decisions: first publish and verify the community directory at its canonical origin, then add its record to `project-site/communities.json` and release the root site. Confirm the community's own sitemap and indexing configuration independently; the root sitemap does not include subdomain pages. Run `npm run project:check` for root-only work and `npm run check -- --all` when shared community rendering or contracts also change.

## Add or correct a food programme

1. Read the selected folder's `AGENTS.md` and review ownership instructions. Check the provider's current official sources and any supporting evidence. Treat submissions and automated findings as leads requiring human review.
Expand Down
2 changes: 2 additions & 0 deletions docs/verification.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Coverage includes schema strictness, evidence/ID relationships, dates, schedules

Browser checks cover no-JavaScript access, small screens, keyboard entry, automated WCAG A/AA checks, local search/category controls, absence of default telemetry/cookies/identifiers, optional aggregate/analytics separation, and the actual operator deployment with outbound test traffic blocked. Chromium, Firefox and WebKit exercise normal and offline navigation and corrupt-data fallback. Chromium additionally closes/reopens a persistent browser process offline and exercises failed release installation, user-approved activation in two tabs, cache cleanup and rollback. These lifecycle tests are intentionally listed once rather than repeated as empty claims for every engine.

The separate root project-site check builds both noindex preview and indexable production fixtures. It verifies the authoritative community projection, ordinary links, canonical/social metadata, sitemap and robots output, provider-alias noindex headers, absence of client analytics/service-worker code, real 404 responses, 320px layout, keyboard skip navigation and no-JavaScript use in Chromium, Firefox and WebKit. These checks establish implementation readiness only; they do not verify DNS, Search Console, hosting redirects or actual indexing.

Inspect generated screenshots in `artifacts/screenshots/`, Playwright's HTML report, retained failure traces and the exact report path printed by the selected build under `artifacts/reports/{deployment_id}/`. Browser profiles, reports and all generated output are private disposable artifacts and excluded from source control. A passing fixture is not evidence that the selected real deployment passed; retain the actual target and source/artifact identity with results.

Automated accessibility checks cannot establish complete accessibility. Before a real production proof, manually review keyboard/focus order, screen-reader announcements, zoom, print and supported phone installation. Verify Android and iOS standalone launch, first-visit offline readiness, reconnection and browser eviction behaviour on actual devices. Desktop WebKit is not proof of every iOS installed-app condition.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
"dev": "node scripts/build.ts --dev",
"build": "node scripts/build.ts",
"preview": "node scripts/serve.ts",
"project:dev": "node scripts/build-project.ts --dev",
"project:build": "node scripts/build-project.ts",
"project:preview": "node scripts/serve-project.ts",
"project:check": "node scripts/check-project.ts",
"project:release": "node scripts/release-project.ts",
"contracts": "node scripts/lib/contracts.ts",
"typecheck": "npm run contracts && tsc --noEmit",
"test": "npm run contracts && node --test tests/unit/*.test.ts tests/contracts/*.test.ts",
Expand Down
16 changes: 16 additions & 0 deletions playwright.project.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: 'tests/e2e',
testMatch: 'project-site.spec.ts',
timeout: 45_000,
expect: { timeout: 12_000 },
fullyParallel: false,
workers: 1,
reporter: [['list'], ['html', { open: 'never' }]],
use: { trace: 'retain-on-failure', screenshot: 'only-on-failure' },
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } }
]
});
Loading
Loading