chore: drop tooling and host references from templates and comments

- The translation issue template called eight locales "AI-initial",
  left Russian out of its list and its dropdown; it now says they
  started as machine-translated drafts reviewed by the community.
- The pull request template asked for two locales where the checks
  require all ten, and pointed at an ignored docs folder.
- Code comments named the maintainer notes file, a release by its
  working name and an internal host; old changelog entries explained
  the notes files by their tool-specific names. A fixture token and
  the deploy webhook test's application id (a real one) are replaced.
This commit is contained in:
Marc-André Bombeck committed 2026-11-12 15:17:06 +01:00
1 parent a10a0e8f84
commit d7eac1f87e
20 files changed
+43 -43

No files matched your search

+3 -2
View File
@@ -1,12 +1,12 @@
name: Translation feedback
description: Report a wording, grammar, or register problem in HealthLog's community-maintained locales (FR / ES / IT / PL / KO / NL / PT-BR).
description: Report a wording, grammar, or register problem in HealthLog's community-reviewed locales (FR / ES / IT / PL / KO / NL / PT-BR / RU).
title: "[Translation] "
labels: ["i18n", "translation-feedback"]
body:
- type: markdown
attributes:
value: |
HealthLog ships German and English as maintainer-curated translations. French, Spanish, Italian, Polish, Korean, Dutch, and Brazilian Portuguese ship as **AI-initial, community-maintained translations** — the prose passes the locale-integrity test (full key parity, no empty values) but has not been hand-reviewed for register or regional vocabulary.
HealthLog ships German and English as maintainer-curated translations. French, Spanish, Italian, Polish, Korean, Dutch, Brazilian Portuguese and Russian started as machine-translated first drafts and are reviewed by the community. They pass the locale-integrity test (full key parity, no empty values), but not every line has been read by a native speaker for register or regional vocabulary.
This template captures concrete fixes you would like to see. Phrasing nuance, regional vocabulary (e.g. "Tú" vs "Usted"), gendered forms, and domain-specific health terms are all in scope. The maintainer applies fixes during the regular release cycle.
@@ -23,6 +23,7 @@ body:
- Korean (ko)
- Dutch (nl)
- Brazilian Portuguese (pt-BR)
- Russian (ru)
- Other (specify under "Surface")
validations:
required: true
+3 -3
View File
@@ -28,16 +28,16 @@
## Checklist
- [ ] Targets the `main` branch (trunk-based — see [CONTRIBUTING.md](../CONTRIBUTING.md))
- [ ] Targets the `main` branch (trunk-based, see [CONTRIBUTING.md](../CONTRIBUTING.md))
- [ ] `pnpm typecheck` passes locally
- [ ] `pnpm lint` passes locally
- [ ] `pnpm test` passes locally
- [ ] `pnpm format:check` passes locally
- [ ] `pnpm build` passes locally
- [ ] User-facing strings go through `t("key")` with both `messages/en.json` and `messages/de.json` updated
- [ ] User-facing strings go through `t("key")`, with the key in all ten `messages/*.json` files
- [ ] No secrets, personal data, or maintainer-name references in committed files
- [ ] Updated `CHANGELOG.md` if user-visible
- [ ] Updated `docs/audit/` or `docs.healthlog.dev` if behavior or self-hosting docs change
- [ ] Updated `docs/` or `docs.healthlog.dev` if behaviour or self-hosting steps change
## Linked issues
+5 -6
View File
@@ -19279,9 +19279,8 @@ NOT NULL`) is the defence-in-depth backstop.
- **DE+EN bilingual CHANGELOG entries (v1.4.14 + v1.4.15)
normalised to English-only.** Per the English-only voice
rule.
- **`CLAUDE.md` filename retired** so the filename is no longer
AI-vendor-specific; `CONTRIBUTING.md` reference updated.
`AGENTS.md` stays for multi-agent compatibility.
- **Maintainer notes renamed**; the `CONTRIBUTING.md` reference is
updated.
### Deferred to v1.4.23
@@ -20599,8 +20598,8 @@ token '<', "<!DOCTYPE "`. 401/403 from the provider now map to 422,
- **Repo-internal docs synced for v1.4.** README adds the
Multi-tenant ready and Test connection buttons feature blocks, the
API reference table includes the eleven new v1.4 endpoints, and
the model count is corrected to 26 (RefreshToken). AGENTS.md and
CLAUDE.md reflect the per-route `/settings/[section]` layout and
the model count is corrected to 26 (RefreshToken). The maintainer
notes reflect the per-route `/settings/[section]` layout and
the per-section admin layout. `docs/api/openapi.yaml` documents
the new endpoints (version, refresh, refresh/revoke,
status-overview, backup/test, the five test-connection probes).
@@ -21157,7 +21156,7 @@ timeout: 30s })` so in-flight handlers finish instead of being killed.
- `.env.example` now points at the in-container hostname `db:5432` (was `localhost:5432`, which never resolves inside the app container).
- **Documentation**:
- `package.json` synced to 1.2.0 (was lagging on 1.1.0).
- `CLAUDE.md` and `AGENTS.md` corrected to 23 models (the `Feedback` model added in v1.2 was missing from the count).
- The maintainer notes are corrected to 23 models (the `Feedback` model added in v1.2 was missing from the count).
- `README.md` Quick Start gives a realistic time estimate, generates the four secrets in one block straight into `.env`, and points reverse-proxy users at the docs.
### Added — Tooling & Supply Chain
+1 -1
View File
@@ -137,7 +137,7 @@ test.describe("/admin/api-tokens mobile no-overflow", () => {
data: [
{
id: "tok1",
name: "iOS auto-login 2026-05-05T19:42:11Z device-AABBCCDDEEFF-marathon",
name: "iOS auto-login 2026-05-05T19:42:11Z device-AABBCCDDEEFF-long-name",
permissions: [
"measurements:write:audit:legacy:extended",
"medications:read",
+1 -1
View File
@@ -221,7 +221,7 @@ async function enterSharedRecord(page: Page): Promise<void> {
}
/**
* Swap the context's pinned locale for one HealthLog ships as an AI-initial
* Swap the context's pinned locale for one HealthLog ships as a machine
* translation, so the maintainership strip has a reason to paint.
*
* The page is parked on `about:blank` for the swap. `I18nProvider` mirrors its
@@ -128,7 +128,7 @@ describe("POST /api/internal/deploy-webhook", () => {
{
status: "success",
application_name: "HealthLog",
application_uuid: "pg8wggwogo8c4gc4ks0kk4ss",
application_uuid: "app-uuid-1",
deployment_uuid: "dep-123",
},
{ "x-deploy-webhook-secret": "test-secret" },
@@ -144,7 +144,7 @@ describe("POST /api/internal/deploy-webhook", () => {
expect.objectContaining({
details: expect.objectContaining({
applicationName: "HealthLog",
applicationUuid: "pg8wggwogo8c4gc4ks0kk4ss",
applicationUuid: "app-uuid-1",
deploymentUuid: "dep-123",
error: null,
raw: expect.objectContaining({ status: "success" }),
@@ -166,7 +166,7 @@ describe("POST /api/internal/deploy-webhook", () => {
{
status: "failed",
application_name: "HealthLog",
application_uuid: "pg8wggwogo8c4gc4ks0kk4ss",
application_uuid: "app-uuid-1",
deployment_uuid: "dep-456",
error: "container exited 137",
},
@@ -284,7 +284,7 @@ describe("POST /api/internal/deploy-webhook", () => {
const fullPayload = {
status: "success",
application_name: "HealthLog",
application_uuid: "pg8wggwogo8c4gc4ks0kk4ss",
application_uuid: "app-uuid-1",
deployment_uuid: "dep-789",
future_field: { commit_sha: "abc123", environment: "production" },
};
@@ -8,7 +8,7 @@ import type { Locale } from "@/lib/i18n/config";
/**
* v1.4.25 — <MaintainershipBanner> contract:
* 1. Renders nothing on maintained locales (DE / EN).
* 2. Renders a notice strip on AI-initial locales (FR / ES / IT / PL).
* 2. Renders a notice strip on machine-translated locales (FR / ES / IT / PL).
* 3. The notice carries a GitHub-issue CTA so feedback has a clear
* home.
*
@@ -61,7 +61,7 @@ describe("<MaintainershipBanner>", () => {
// documents that the strip is NOT painted before the first effect
// runs — exactly the contract that prevents the flash-of-banner
// problem.
it("paints nothing in the SSR pass for AI-initial locales", () => {
it("paints nothing in the SSR pass for machine-translated locales", () => {
for (const locale of ["fr", "es", "it", "pl"] as const) {
const html = ssr(locale);
expect(html).not.toContain("maintainership-banner");
@@ -6,11 +6,11 @@ import { isMaintainedLocale, type Locale } from "@/lib/i18n/config";
import { useTranslations } from "@/lib/i18n/context";
/**
* v1.4.25 — notice strip for AI-initial locales.
* v1.4.25 — notice strip for machine-translated locales.
*
* HealthLog ships German + English as maintainer-curated translations
* (the maintainer owns both bodies). French, Spanish, Italian
* and Polish ship as AI-initial translations — they pass the
* and Polish ship as machine translations — they pass the
* locale-integrity test (full key parity, no empty values, no TODOs)
* but the prose has not been hand-reviewed for register, regional
* vocabulary, or domain-specific nuance.
@@ -18,7 +18,7 @@ import { useTranslations } from "@/lib/i18n/context";
* This banner renders ONLY on non-maintained locales (FR/ES/IT/PL/KO
* today) and surfaces a small, dismissible strip at the top of the
* authenticated app shell. It tells the user the translation is
* AI-maintained and links to the GitHub translation-feedback issue
* machine-translated and links to the GitHub translation-feedback issue
* template so they can contribute fixes back.
*
* Dismissal is persisted to localStorage per-locale so a user who
+1 -1
View File
@@ -31,7 +31,7 @@ describe("parseLocaleFromAcceptLanguage", () => {
expect(parseLocaleFromAcceptLanguage("*")).toBe("en");
});
// v1.4.25 — the four AI-initial locales added in this release.
// v1.4.25 — the four machine-translated locales added in this release.
// Each maps via the same primary-tag prefix match the DE branch uses.
it("returns the matching tag for FR / ES / IT / PL", () => {
expect(parseLocaleFromAcceptLanguage("fr-FR,fr;q=0.9")).toBe("fr");
+2 -2
View File
@@ -450,7 +450,7 @@ export async function extractAndStoreFacts(
let proposed = 0;
for (const f of toStore) {
// Field-by-field, no spread (mass-assignment rule, CLAUDE.md). A health
// Field-by-field, no spread (no mass assignment). A health
// fact waits for the person's tap; everything else is kept as extracted.
const source = backgroundSource(f.category, f.fact, "extracted");
if (source === PROPOSED_FACT_SOURCE) proposed += 1;
@@ -710,7 +710,7 @@ export async function storeDeterministicFacts(args: {
for (const cand of candidates) {
if (isNearDuplicate(cand.fact, seen)) continue;
seen.push(cand.fact);
// Field-by-field, no spread (mass-assignment rule, CLAUDE.md).
// Field-by-field, no spread (no mass assignment).
await db.coachFact.create({
data: {
userId: args.userId,
+1 -1
View File
@@ -380,7 +380,7 @@ export async function extractAndStorePlanProposals(
const toStore = accepted.slice(0, remainingCapacity);
for (const p of toStore) {
// Field-by-field, no spread (mass-assignment rule, CLAUDE.md). Always
// Field-by-field, no spread (no mass assignment). Always
// written as "proposed" — only the user-facing PATCH activates a plan.
await db.coachPlan.create({
data: {
@@ -18,7 +18,7 @@ import { loadSafetyContracts } from "@/lib/ai/prompts/safety-contracts";
const NATIVE_LOCALES = ["fr", "es", "it", "pl"] as const;
/**
* v1.4.25 — each AI-initial locale (FR / ES / IT / PL) now gets a
* v1.4.25 — each machine-translated locale (FR / ES / IT / PL) now gets a
* native system prompt assembled from the safety-contract matrix
* instead of riding the EN body with a one-line footer.
*
@@ -34,7 +34,7 @@ const NATIVE_LOCALES = ["fr", "es", "it", "pl"] as const;
* 9. The reply-language directive lives at the foot of the prompt.
*/
describe("getCoachSystemPrompt — native AI-initial locales", () => {
describe("getCoachSystemPrompt — native machine-translated locales", () => {
it.each(NATIVE_LOCALES)(
"%s produces a non-empty native prompt embedding PROMPT_VERSION",
(locale) => {
@@ -140,7 +140,7 @@ describe("getCoachSystemPrompt — native AI-initial locales", () => {
});
});
describe("getStrictInsightsSystemPrompt — native AI-initial locales", () => {
describe("getStrictInsightsSystemPrompt — native machine-translated locales", () => {
it.each(NATIVE_LOCALES)(
"%s produces a non-empty native Insights prompt",
(locale) => {
+2 -2
View File
@@ -1032,7 +1032,7 @@ export function buildSystemPromptWithReferences(
const refs = selectReferencesForMetrics(metrics);
if (refs.length === 0) return base;
// v1.4.25 — AI-initial locales (FR/ES/IT/PL) ride the EN sources
// v1.4.25 — machine-translated locales (FR/ES/IT/PL) ride the EN sources
// block. The "title" field is the EN title; the model is told to
// surface the citation id, not to translate the source title, so this
// is safe — citation labels in the UI use the contract id (lowercase,
@@ -1107,7 +1107,7 @@ export const OUT_OF_SCOPE_REFUSAL_DE = {
* from the safety-contract matrix so the summary copy stays in sync
* with the prompt body that taught the model to emit it. The EN /
* DE constants above remain authoritative for their locales — these
* helpers cover the AI-initial locales.
* helpers cover the machine-translated locales.
*/
export const OUT_OF_SCOPE_REFUSAL_FR = buildOutOfScopeRefusal("fr");
export const OUT_OF_SCOPE_REFUSAL_ES = buildOutOfScopeRefusal("es");
@@ -171,7 +171,7 @@ describe("generateAlerts", () => {
});
});
// v1.4 marathon — closes the body-fat ACE drift identified in the
// v1.4: closes the body-fat ACE drift identified in the
// medical-truthfulness audit. Below-essential is now a danger band,
// "Essential" is the warning band proper, and the labels match ACE.
describe("classifyBodyFat (ACE bands)", () => {
+1 -1
View File
@@ -480,7 +480,7 @@ async function lookupIpOnline(ip: string): Promise<GeoResolved | null> {
// metadata address (SSRF/rebinding).
// v1.15.12 — keep the online timeout below the audit-log 3 s race
// window so a slow-but-successful lookup still lands before the race
// resolves null and drops the location (the "—" symptom on apps01).
// resolves null and drops the location (the "—" symptom in production).
{ timeoutMs: 2_500, requirePublicHost: true },
);
if (!res.ok) {
+1 -1
View File
@@ -87,7 +87,7 @@ export const localeLabels: Record<Locale, string> = {
/**
* v1.4.25 — locales actively maintained by the project owner.
* Locales outside this set are AI-initial translations and surface a
* Locales outside this set are machine-translated translations and surface a
* <MaintainershipBanner> notice at the top of the auth shell so users
* know to expect rough edges and where to contribute fixes. Promoting
* a locale here turns the banner off.
@@ -33,7 +33,7 @@ describe("determinePhase", () => {
});
});
// v1.4 marathon — closes v3 audit "Locale Mixing" CRIT.
// v1.4: closes the "Locale Mixing" finding.
// Reminder messages must follow the user's stored locale, not always
// German. callback_data values are stable English identifiers and stay
// untranslated regardless of UI locale.
+4 -4
View File
@@ -1,10 +1,10 @@
/**
* Host-load sampler (every five minutes since v1.42) that backs the admin /admin/system-status
* "last 2 hours" chart (v1.4.16 phase B3).
* "last 2 hours" chart.
*
* The maintainer asked for a small at-a-glance graph above the system-status facts
* grid showing CPU + memory + disk-IO so a glance at /admin tells them
* whether the apps-01 host is healthy. We considered scraping Coolify's
* A small at-a-glance graph above the system-status facts grid shows
* CPU + memory + disk-IO, so a glance at /admin tells an operator whether
* the app host is healthy. We considered scraping Coolify's
* Sentinel collector, but the Coolify HTTP API does not expose a clean
* read endpoint for per-server metrics — every approach ended up either
* scraping the Coolify dashboard HTML or hitting a private RPC. An
+2 -2
View File
@@ -157,8 +157,8 @@ const resolveLocale = coerceLocale;
* `medicationReminders.phase*`. The reminder worker passes the user's
* stored locale; absent / unknown locales fall back to the app default.
*
* v1.4 marathon — closes the v3 audit "Locale-Mixing" CRIT finding
* (German-only Telegram / Web Push templates regardless of user locale).
* v1.4: closes the "Locale-Mixing" finding (German-only Telegram / Web Push
* templates regardless of user locale).
*/
export function getPhaseMessage(
phase: ReminderPhase,
+2 -2
View File
@@ -175,8 +175,8 @@ function isPublicPath(pathname: string): boolean {
* a unit toggle and a date of birth, and it is why free text, anything that
* reaches an AI prompt, and anything creating a second record are refused.
*
* This whole block only runs under `DEMO_MODE=true`, so production (apps01)
* is unaffected by construction.
* This whole block only runs under `DEMO_MODE=true`, so production is
* unaffected by construction.
*/
const DEMO_MUTATION_ALLOWLIST: ReadonlyArray<{ path: string; method: string }> =
[