feat(narratives): add config contract, static assets and lockfile - #439
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces configuration schemas, examples, and documentation for customizing Data Commons instances, including support for branding, metrics, and agent settings. It also updates the useBranding hook and its tests to support the new logo configuration key alongside its legacy alias. The review feedback suggests tightening the regex validation for fiscal_year_start in the agent configuration schema and adding a test case to cover the public-bucket fetching path in the branding hook tests.
Makes the narratives app deployable from a clean clone. Three related gaps:
- `package-lock.json` was never committed, so `npm ci` — used by the image
build — failed outright. All 25 dependencies float on `^` ranges, so pinning
the tree is what makes a clone reproduce the tested build.
- `public/` was missing entirely while four code paths referenced it:
`/send.svg` (view_initial, data_agent), `/loader.png` (block_reasoning) and
`/logo.png` (header, as a fallback). Those requests 404'd.
- `config/` documents the per-instance contract — the branding and agent
schemas plus fillable examples — which the agent and UI read from the
instance's config bucket at runtime. Instance-specific values stay out of the
repo: copy the `.example.json` files and edit them.
Also fixes the branding logo, which never loaded from config. The schema
publishes `logo`, but the UI read only `logo_url`, so `logoUrl` stayed empty and
the header always took the bundled fallback. `mapRawToBranding` now reads `logo`
and keeps `logo_url` as a legacy alias, matching how the same file already
accepts legacy aliases for colors, fonts and `suggestion_chips`. Covered by
tests that fail without the fix.
Overriding the logo stays optional: no logo ships in the config example, so a
fresh instance renders the bundled `public/logo.png`. An instance that wants its
own adds a `logo` key and uploads the image to its config bucket, which
`config/README.md` documents.
The branding schema previously declared eight keys with no consumer anywhere in
the UI — `favicon`, `logo_alt`, `extra_css`, `footer`, `splash_assets`,
`instance_id`, `domain_hint` and `analytics`. They are omitted here rather than
carried over, so every key in the schema is one the UI actually reads.
`template_vars` is kept but now says plainly that placeholder rendering is not
implemented; the previous wording claimed the agent already substituted
`{{instance.*}}`, which it does not.
67b1632 to
bef8ea0
Compare
juliawu
left a comment
There was a problem hiding this comment.
Thank you for the cleanup! Just a few comments. I also think the gemini review comments are worth looking at.
There was a problem hiding this comment.
Can be changed later, but @miss-o-soup should we have a different "default" logo in the main repo?
…tests
Review follow-ups on the config contract:
- API keys are no longer part of the config contract at all. `gemini.api_keys`,
`demo_api_keys` and the legacy scalar `api_key` are removed from the schema and
the example. `gemini` is `additionalProperties: false`, so adding them back is
a validation error — which is the point: the file stays safe to commit and to
serve from a config bucket, and a key cannot leak by being pasted into it.
A deployed instance resolves keys from Secret Manager via
`GEMINI_API_KEYS_SECRET` / `GEMINI_DEMO_API_KEYS_SECRET`.
The agent keeps reading `gemini.api_keys` from a local, uncommitted
`config.json` so local development still works, but that path now logs a
warning naming the environment variable to set. Previously it succeeded
silently, so a deployment reading keys from config looked healthy.
`get_api_keys`'s docstring also claimed the scalar `gemini.api_key` was
"rejected outright" when the code accepted it; it now describes what the code
does.
- `template_vars.fiscal_year_start` had pattern `^[0-9]{2}-[0-9]{2}$`, which
accepted `99-99` and `00-00`. Now bounds the month to 01-12 and the day to
01-31.
- Two tests cover the public-bucket path, which had none: `/agent/brand`
returning only `brand_config_url` and the browser fetching
`<bucket>/branding.json` itself, plus the 404 case falling back to defaults.
- The header's logo alt text hardcoded one instance's name as its fallback.
It now uses the configured instance name, or a neutral string when unset.
Makes the narratives app deployable from a clean clone. Three related gaps:
package-lock.jsonwas never committed, sonpm ci— used by the image build — failed outright. All 25 dependencies float on^ranges, so pinning the tree is what makes a clone reproduce the tested build.public/was missing entirely while four code paths referenced it:/send.svg(view_initial, data_agent),/loader.png(block_reasoning) and/logo.png(header, as a fallback). Those requests 404'd.config/documents the per-instance contract — the branding and agent schemas plus fillable examples — which the agent and UI read from the instance's config bucket at runtime. Instance-specific values stay out of the repo: copy the.example.jsonfiles and edit them.template_varsis kept but now says plainly that placeholder rendering is not implemented; the previous wording claimed the agent already substituted{{instance.*}}, which it does not.