# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie).


## 0.3.6 - 2026-04-16
### Changed
* syncDocs now regenerates the docs-site package-lock.json after bumping devDependencies.gina — prevents CI / Vercel / Worker deploy failures on every stable release.
### Fixed
* Prevented spurious Whisper Error on the first CLI command after a fresh install (#B12). `gina --version` and `gina framework:*` commands no longer emit a red error stack trace before their output on a brand-new install.
* Hardening framework:init against missing or mistyped `def_*` keys in ~/.gina/main.json (#B13 — blast-radius follow-up to #B12). `main['def_prefix']`, `def_global_mode`, `def_arch`, `def_platform`, `def_env`, `def_scope`, `def_log_level` reads now short-circuit to undefined instead of throwing TypeError when the key is entirely absent.
* Surfacing the real cause when `prepare_version.js` is run against a stale `~/.gina/<release>/settings.json` `dir` field. Publish now fails fast with an actionable message instead of wedging at `pushChangesToGitIfNeeded` with a misleading "No branch selected" error.
* Preventing CORS preflight failure when a bundle's env.json `access-control-allow-headers` list omits a header the client actually sends. `completeHeaders()` no longer overwrites the echo that `checkPreflightRequest()` sets from the incoming `access-control-request-headers` (#B13).
### Security
* Added pre-commit hook (.githooks/pre-commit) that blocks Claude-related paths (CLAUDE.md, .claude*) from entering git history. post_install.js installs core.hooksPath=.githooks for contributor clones only (gated on .git presence in gina repo root). #S5 follow-up to #S3.
* Added GitHub Actions workflow (.github/workflows/security.yml) that greps the git index on every push/PR to develop and master for Claude-related paths (CLAUDE.md, .claude*). Fails the build if any are tracked. Backs up the local pre-commit hook (#S5) and publish-boundary gate (#S3). #S6 follow-up to #S3.
* Added private-token leak gate (script/check_no_claude_leak.js) wired into the npm prepack hook and the prepare_version.js self.* chain before any git add --all. Fails the publish if the tarball listing contains Claude-related paths OR if pack contents contain private-token patterns (phone, private email, private address, private domain, co-author legal name). #G1 follow-up to #S3/#S4.
* Added opt-in pre-commit hook (resources/git-hooks/pre-commit) that blocks commits authored under a private-domain git identity (.local hostnames, internal domains). Contributors install it once via cp resources/git-hooks/pre-commit .git/hooks/pre-commit. Complements the CI-side Claude-path guard (#S5/#S6). #G5.
* Redacting secret-looking fields from the dev-mode Inspector feed before any sink (window.__ginaData, localStorage.__ginaData, /_gina/agent SSE, engine.io push, ginaToolbar forms/XHR overlays). New lib/inspector-redact deep-clones the Inspector payload and replaces values whose keys match the default regex (password, pwd, secret, token, apikey, cvv, ssn, authorization, credentials, private_key) with [redacted]. Two complementary carve-outs preserve validation metadata: (1) NON_SECRET_SUFFIX — keys ending in rule/rules/policy/policies/validator/config/configuration/settings/requirements/strength/constraint/options/schema/definition/spec pass through untouched (e.g. passwordRule, passwordPolicy describe form rules, not user input); (2) primitive-only redaction — when a matched secret key holds an object or array, the walker recurses into it instead of replacing it, because such values are always metadata (e.g. rules.account[password] = {isRequired: true, isString: 7} — an HTML form field name under a rules: section). Nested primitive secrets inside preserved objects are still caught on recursion. Client-side shim in statusbar.html mirrors both carve-outs and additionally redacts any DOM input whose type matches configured types (default: password). Configurable via settings.json inspector.redact.{patterns,types,replacement}. Dev-mode only — the actual HTTP response body is never touched. #R7.

## 0.3.5 - 2026-04-13
### Security
* Bumped @rhinostone/swig to 1.5.0 — extends CVE-2023-25345 path-traversal guards to bracket notation, set bracket assignment, for loop variables, macro names, and import aliases.
* Updated vendored client-side swig (core/deps/swig-client/) to the v1.5.0 browser build. Extends CVE-2023-25345 path-traversal guards to bracket-notation access, for-loop variables, macro names, and import aliases — completing the __proto__/constructor/prototype blocklist. Browser-side templating now matches the server-side @rhinostone/swig 1.5.0 protections.

## 0.3.4 - 2026-04-12
### Fixed
* bumpVersion and prepare_version now update gna.js framework paths on version changes — fixes CI test failures where gna.js referenced a stale framework directory
* Swig resolution test now skips on CI where framework/v*/node_modules does not exist (framework/package.json is gitignored)
* require('gina/gna') explicit exports now resolve correctly — v0.3.3 shipped with stale framework paths in gna.js that caused MODULE_NOT_FOUND errors

## 0.3.3 - 2026-04-11
### Added
* Live database index introspection for Inspector Query tab — SQL connectors (MySQL, PostgreSQL, SQLite) now report actual database indexes without requiring manual indexes.sql files
* Implement `framework:get` CLI command — read one or all keys from ~/.gina/settings.json (`gina get --key`, `gina get all`). Completes the `gina set` / `gina get` pair.
* Implement `port:set` CLI command — set or update a port number for a bundle/environment/protocol/scheme combination. Supports both positional syntax (`gina port:set http/1.1:3200 frontend @myproject/dev`) and flag syntax (`--protocol=`, `--scheme=`, `--port=`, `--env=`), with interactive prompts for missing values.
### Changed
* Improved popin loading and opening performance: parallel resource loading via DOM injection (replaces sequential XHR + eval), eliminated redundant DOM binding on open, reduced DOM scans with querySelectorAll, classList API, cached RegExp, lightweight ID counter, per-load XHR for concurrent popins
* Replacing uuid/crypto.randomUUID() with internal lib/uuid — lightweight base-62 ID generator using crypto.getRandomValues with bitmask bias avoidance, zero external dependencies
* Replaced vendored swig-1.4.2 with @rhinostone/swig npm dependency in framework/v*/package.json
### Fixed
* Fixed malformed regex in events.js XHR error handler, operator precedence bug in binding.js error logging, typo in events.js header loop variable, implemented popinDestroy (was a stub), and fixed registeredPopins duplicate-check array never being populated
* explainForIndexes crashes on bulkInsert path where conn._cluster is undefined (conn is a scope object, cluster lives at conn._scope._bucket._cluster). The crash occurs before self.emit(trigger), silently hanging the entity callback and causing a 504 timeout with no error in logs. Fixed by resolving the cluster from either conn shape, with early-return guard and internal try/catch so dev-mode instrumentation never blocks the emit path.
* Validator global validation pass no longer displays errors for untouched fields. On every field blur, the validator runs a full-form validation to determine submit button state (isFormValid). Previously, the global pass displayed errors for ALL invalid fields — untouched fields would suddenly show errors when the user interacted with a different field. Now only the field the user actually touched (event.target.name) shows errors. Two call sites fixed in validator/src/main.js (~line 3337 and ~line 4865).
* Vendoring streamsearch-1.1.0 to fix busboy MODULE_NOT_FOUND crash in Docker containers after framework directory rename
* Forwarding emerg message to CLI output and docker logs when a bundle aborts during startup
* Config loader checks MIDDLEWARE file existence before reading — prevents crash in containers or environments where the file is absent
* requireJSON now tolerates trailing commas in JSON config files — strips them and warns instead of calling emerg+exit, preventing bundle abort on common authoring mistakes

## 0.3.3-alpha.3 - 2026-04-09
### Added
* TypeScript declaration files (`types/index.d.ts`, `types/globals.d.ts`, `types/gna.d.ts`) — `.d.ts` declarations for the public surface: `SuperController`, `EntitySuper`, global helpers, connector config shapes, `routing.json` schema, all config file interfaces, `GinaRequest`/`GinaResponse` types, `PathObject`, `uuid`, and the `Gna` lifecycle module. Zero TS migration of internals — declarations only. Enables IDE autocomplete and static analysis for consumer projects. `package.json` wired with `"types"` and `"typesVersions"`.
* Explicit exports via `require('gina/gna')` — all global helpers (`getContext`, `setContext`, `_`, `requireJSON`, `onCompleteCall`, `getModel`, `uuid`, `ApiError`, etc.), `SuperController`, and `EntitySuper` available as named imports. Uses lazy getters so symbols resolve at access time (after framework boot). Enables IDE go-to-definition and `import { getContext } from 'gina/gna'`-style usage alongside the existing global injection.

## 0.3.3-alpha.2 - 2026-04-08
### Added
* `bundle:openapi` CLI command — generates an OpenAPI 3.1.0 specification from `routing.json` for one or more bundles. Alias: `bundle:oas`. Supports `--output` flag for custom output path. Zero manual spec writing — `url`, `method`, `param.control`, `namespace`, `requirements`, `_comment`, `_sample`, `middleware`, and `cache` fields all map to their OpenAPI equivalents. Makes any Gina app consumable by AI agents, API gateways, and testing tools.
* `framework:get` CLI command — read one or all keys from `~/.gina/settings.json`
* `port:set` CLI command — set or update a specific port for a bundle/env/protocol/scheme combination

## 0.3.2 - 2026-04-07
### Added
* JSON Schema files for all config files (app, connectors, manifest, routing, settings, watchers, crons) — published at gina.io/schema/*
* Entity short-name aliases — use self.getEntity('user') instead of self.getEntity('user/user')
### Changed
* Server engines — font MIME types (woff/woff2/otf/ttf) and Inspector endpoint improvements
* Inspector UI — tab layout presets (Balanced/Backend/Frontend/Custom with drag-to-reorder), query performance banners, missing-index banners, cross-bundle QI propagation, render-json Inspector data feed, _inspectorActive profiling gate
### Fixed
* Model loading without onInitialize hook — entities load correctly when the model has no onInitialize
* CLI stability and install path edge cases
* getConfig() proxy override — guards against undefined PROXY_HOSTNAME on same-origin POST requests with Origin header
* bundle:start crash on unregistered bundle — isDefined guard moved before property access

## 0.3.1 - 2026-04-06
### Added
* SQL index reporting Phase A (#QI1): connectors read indexes.sql at startup and populate query instrumentation index badges in the Inspector
### Changed
* HTML rendering now uses direct HTTP/2 stream.respond() + stream.end(), bypassing the HTTP/1.1 compatibility layer (matching JSON rendering behaviour)
* Removed ssl-checker, colors, and uuid dependencies — engine.io is now the sole runtime dependency

## 0.3.0 - 2026-04-06
### Added
* HTTP/2 client resilience: retry with backoff (up to 2 retries with 500ms delay), pre-flight PING validation on cached sessions, and session freshness tracking via _lastPongAt. Protects against OrbStack silent TCP drops and stale HTTP/2 sessions.
* Inspector UX: drag-to-select log rows, copy badge fade-out, left accent selection styling, logo watermark on content panes, window geometry and env panel height persistence
* Inspector View tab performance anomaly alerts: pulsing dot indicator on tab label, badge border override, and tooltip for metrics exceeding thresholds (load time, transfer size, FCP, query duration, query count)
* Inspector Query tab: index badges per query card showing which database indexes are used (green for secondary, amber for primary scan, red for no index, grey N/A for unsupported connectors)
* Inspector Query tab: per-card result size color-coding using the same weight thresholds as the total badge
* Inspector Query tab: search input debounce (200ms), filter persistence in localStorage, and pagination (20 cards default with Show all button)
* Couchbase connector: extractIndexes() helper and profile timings query option in dev mode for index reporting in the Inspector
* Inspector: manual connect form on the 'No source' overlay — type a bundle URL to connect without editing the address bar
* Inspector: tab layout presets (Balanced / Backend / Frontend / Custom) in settings panel — reorder tabs for different developer audiences, or drag to create a custom order
* Docker Compose starter: `docker compose up` in gina-starter sets up a Gina app container + Couchbase Community 7.6.4 with pre-seeded cluster, bucket, application user, and sample documents — zero local install required
### Changed
* Renamed legacy Inspector parameter and marker names across the render pipeline: `displayToolbar` → `displayInspector` (server.js, controller.js, render-swig.js, render-v1.js), `{# Gina Toolbar #}` → `{# Gina Inspector #}` comment markers and guard regexes, "Beemaster" → "Inspector" in server.isaac.js and scan.js comments. `render-v1.js` now includes `statusbar.html` instead of `toolbar.html`, completing the migration of all render paths to the modern Inspector statusbar.
### Fixed
* Fixed error page displaying `[object Object]` when upstream returned an error object instead of a string — render-swig now normalises error and message fields to strings before building the error response (#Q2)
* Couchbase session store v3/v4: touch() and destroy() passed MutationResult as error to express-session callback, causing defer(next, MutationResult) and a 500 response with the Couchbase CAS token as body on every authenticated read-only request (#CB-BUG-4)
* Static file server now sends ETag and Last-Modified headers in production, enabling conditional GET (304 Not Modified) responses for all static types on both HTTP/1.x and HTTP/2 paths
* Fixed method-based routing on shared URLs — the router now continues scanning after a URL match with a wrong method instead of immediately throwing 405. GET /notes and POST /notes can coexist as separate routing rules; a 405 is only emitted after the full scan finds no method match.
* Fixed crash in fitsWithRequirements when a URL param route has no requirements block — router now accepts any non-empty segment value and correctly binds it to req.params / req[method] instead of throwing TypeError.
* Fixed throwError(res, code, stringMsg) silently overriding an explicit 3-digit HTTP status code with res.status || 500 — throwError(res, 404, "Not found") now correctly sends 404 instead of 500.
* Removed dead buffer consumption code from entity.js Option B path (_arguments[trigger] was unreachable for both connector-generated and custom entity methods). Added defensive unconditional clear at Option B entry point to prevent stale buffer leakage across calls (#M5).
* Inspector Query tab: durationClass variable shadowing in renderQueryContent

## 0.3.0-alpha.1 - 2026-04-02
### Added
* `project:start` / `project:stop` / `project:restart` — start, stop, or restart all bundles in a project with one command. Delegates to `bundle:start/stop/restart @<project>` (bulk mode). Flags forwarded.
* Inspector Phase 2 — Inspector dev SPA embedded at `/_gina/inspector/` (engine-agnostic — works with both Isaac and Express). See CHANGELOG.md for the complete breakdown.
* Thin in-page status bar — `statusbar.html` (new file in `dist/vendor/gina/html/`) is included instead of `toolbar.html`. A 36-line vanilla JS IIFE creates a Shadow DOM host (`#__gina-statusbar`) fixed at bottom-right: status dot (green = ok, red = `data.error` set), `bundle@env` label, and an "Inspector" link to `/_gina/inspector/`. No RequireJS, no jQuery, no SASS. Shadow DOM isolates styles from the app. `gina.js` toolbar module silently skips init when `#gina-toolbar` is absent (`!$toolbar.length` guard at line 17752).
* Gina infrastructure port range 4100–4199 — reserved exclusively for Gina infrastructure; never assigned to bundle HTTP servers by `gina port:scan` (range skipped automatically). Layout: `4100` = socket server (future migration from 8124), `4101` = reserved, `4102` = engine.io internal transport, `4103–4199` = reserved for future Gina infrastructure. `settings.json` `engine.io.port` corrected from `8888` (conflicts with Jupyter Notebook) to `4102`. Port range documented in `framework/v0.3.0-alpha.1/lib/cmd/port/help.txt`.
* `window.__ginaData` — replaces the two `<pre id="gina-toolbar-*-json">` DOM nodes with a single `<script>window.__ginaData = { gina: {...}, user: {...} };</script>` tag injected before `</body>` in dev mode. Data is built in Node.js (`controller.render-swig.js`) with the same field masking as before (`view.assets = {}`, `view.scripts/stylesheets = "ignored-by-toolbar"`); `</script>` and `<!--` sequences are escaped before serialization. Toolbar JS (`gina.js` dist + `main.js` src) updated to read from `window.__ginaData` directly instead of scraping DOM text nodes; mock file-upload writes back to `window.__ginaData.user`/`.gina`. The Inspector will access this object via `window.opener` or `postMessage` when it opens as a separate tab. No change to the data structure or toolbar panel rendering logic.
* PATCH method support — `req.patch` is now populated with the parsed request body (JSON or form-encoded). Body parsing mirrors POST. `req.body` aliases `req.patch` for method-agnostic actions. URI params are merged into `req.patch` the same way as PUT. Use PATCH for partial updates (only the sent fields change); use PUT for full resource replacement. `case 'patch':` added to `getParams()` and `getParam()` in `controller.js`. `"method": "PATCH"` is now valid in `routing.json`.
* HEAD method support — `req.head` is populated with query-string and URI params (same semantics as GET). The framework runs the full controller action so all response headers are set correctly, then suppresses the body before writing to the wire. Both `render()` (Swig) and `renderJSON()` honour HEAD. Routes declared as `GET` automatically accept HEAD requests — no separate routing rule needed. Useful for cache validation, existence checks, and CDN probing without downloading a response body.
* Async controller actions — controller actions can be declared `async`. The router captures the return value of `controller[action]()` at both dispatch sites (with and without middleware) and attaches `.catch()` if the value is thenable. Rejections are routed to `throwError(response, 500, err.stack || err.message || String(err))`. Existing sync actions are unaffected.
* `onCompleteCall(emitter)` global helper — wraps any EventEmitter that exposes `.onComplete(cb)` into a native Promise. Enables `await onCompleteCall(_(path).mkdir())` and similar patterns for PathObject file operations (`mkdir`, `cp`, `mv`, `rm`) and `Shell` commands from async controller actions.
* Fixed empty `unhandledRejection` handler in `gna.js` — now logs `[ FRAMEWORK ] Unhandled promise rejection:` with the full stack so stray rejections are visible in framework logs instead of being silently swallowed.
* 13 new unit tests covering the async dispatch guard source structure and pure `dispatchAction` logic (test/core/router.test.js suites 06–07).
* `renderStream(asyncIterable, contentType)` on SuperController — streams any `AsyncIterable` as a chunked HTTP response without buffering. `contentType` defaults to `text/event-stream` (SSE); each yielded string/Buffer becomes `data: {chunk}\n\n`. For other content types, chunks are written raw. HTTP/2: `stream.respond()` + `stream.write()` + `stream.end()`. HTTP/1.1: automatic chunked transfer-encoding via `response.write()`. `x-accel-buffering: no` set automatically for SSE. Upstream response headers (CORS etc.) merged into the initial headers frame. Required for LLM token streaming with `ai.client` + `stream: true`. 32 unit tests (test/core/render-stream.test.js).
* Route radix trie (#R1) — `lib/routing/src/radix.js` builds a segment-level radix trie from `routing.json` once at startup. `Routing.buildTrie(routing, bundle)` is called in `onRoutesLoaded()`; `Routing.lookupTrie(pathname, bundle)` returns candidate route names in O(m) time (m = path segment count). On a cache miss, `handle()` builds a `Set` from the trie candidates and uses `Set.has()` to skip non-matching routes in the `for…in` loop — turning the worst-case O(n×m) scan into O(m) candidate selection + O(k×m) validation (k = candidates). Falls back transparently to the full scan when the trie is unavailable. Internal change — no user-facing API change. 46 unit tests (test/lib/radix.test.js).
* HTTP/2 configurable settings (#H3) — `maxConcurrentStreams` and `initialWindowSize` are now read from `settings.json` `http2Options` instead of being hardcoded. Defaults: `maxConcurrentStreams: 256`, `initialWindowSize: 655350` (10× TCP default). Security-critical settings (`maxHeaderListSize: 65536`, `enablePush: false`, `maxSessionRejectedStreams: 100`, `maxSessionInvalidFrames: 1000`) remain hardcoded and are not user-overridable. 16 unit tests in test/core/server.isaac.test.js (suites 03–03b).
* HTTP/2 session metrics (#H3) — `server._h2Metrics` counter object (`activeSessions`, `totalStreams`, `goawayCount`, `rstCount`) is instrumented via `server.on('session', …)` event handlers and exposed under the `"http2"` key in the `/_gina/info` JSON response. 18 unit tests in test/core/server.isaac.test.js (suites 04–04b).
* Security & CVE compliance docs page — `docs/security.md` lists all known HTTP/2 CVEs addressed by Gina: CVE-2023-44487 (Rapid Reset), CVE-2024-27316 / CVE-2024-27983 (CONTINUATION flood), CVE-2019-9514 (RST flood), HPACK bomb, and server push abuse. Each section includes attack description, Gina mitigation code snippet, and the Node.js version required. Docs only — no code changes.
* AI connector — declare any LLM provider in `connectors.json` via a named protocol (`anthropic://`, `openai://`, `deepseek://`, `qwen://`, `groq://`, `mistral://`, `gemini://`, `xai://`, `perplexity://`, `ollama://`). All OpenAI-compatible providers share the `openai` npm package; `baseURL` overrides let any endpoint work including Ollama (MiMo, Llama, Phi…) and self-hosted vLLM. Unified `.infer(messages, options)` normaliser returns `{ content, model, usage, raw }` across providers; raw SDK client exposed on `.client` for streaming and function-calling. No tokens spent at startup. 70 unit tests (test/core/ai-connector.test.js).
* `peerDependencies` for AI SDK clients — `openai` (>=4.0.0) and `@anthropic-ai/sdk` (>=0.27.0) added as optional peer dependencies alongside the existing database connector peers (`ioredis`, `mysql2`, `pg`, `mongodb`, `@scylladb/scylla-driver`, `couchbase`). npm/yarn will surface a compatibility warning when a user pins an untested client version. All clients remain loaded from the project's `node_modules` — zero framework runtime dependency.
* MySQL ORM connector — entity wiring + SQL method generation from `sql/` files, `?` positional placeholders, `mysql2` pool (loaded from project `node_modules` — zero framework dependency), native Promise + `.onComplete()` shim, `@param`/`@return` annotation support. Install `mysql2` in your project to activate. 68 unit tests (test/core/mysql-connector.test.js).
* PostgreSQL ORM connector — same entity/SQL architecture as MySQL; uses `$1`, `$2`, … placeholders, `pg.Pool` with idle-client error guard, `pool.query()` execution. Install `pg` in your project to activate. 72 unit tests (test/core/postgresql-connector.test.js).
* Per-bundle framework version pinning — declare `"gina_version": "<version>"` on any bundle entry in `manifest.json` to run that bundle under a specific installed gina version. The socket server keeps running its own version; only the spawned bundle process uses the declared version. Validated against the tracked version registry in `~/.gina/main.json` before start.
* `--gina-version=<version>` flag for `bundle:start` — CLI override that takes priority over `manifest.json`. Passes the version down to the spawned process via an isolated context clone so concurrent bundle starts do not interfere.
* `bundle:add` now writes `"gina_version"` to the new bundle entry in `manifest.json` — records the current framework version at scaffold time so the pin is explicit from day one.
* 33 new unit tests covering the resolution logic, validation, context isolation, and source structure (test/lib/bundle-gina-version.test.js).
* Auto-sync `def_framework` in `~/.gina/main.json` on `bundle|project start|restart` (`bin/cli`) and on `gina-container` startup — keeps the active framework version aligned with `package.json` automatically after a version bump or worktree branch switch, with no manual `main.json` edit required.
* Added `def_framework` sync in `post_install.js` — `main.json` is updated at install time so the CLI, cmd handlers, and `framework:init` all agree on the active version immediately after install.
* New unit tests for `def_framework` sync logic in `test/bin/cli.test.js` (9 tests) and `test/bin/gina-container.test.js` section 05 (10 tests).
* Hot-reload (#M6) — in dev mode `WatcherService` now starts automatically and registers watchers for `controller.js`, `controller.render-swig.js`, and the bundle's `controllers/` directory. `require.cache` is evicted only when a watched file has actually changed, eliminating per-request eviction overhead. Falls back transparently to per-request eviction when the watcher is not available (production, non-dev env). 15 new unit tests covering source-structure guards and pure dirty-flag logic (test/core/router.test.js suites 08–09).
* GOAWAY logging (#H5) ��� `client.on('goaway')` handler in `controller.js` now logs `errorCode`, `lastStreamID`, and session key via `console.warn`. Distinguishes clean server restarts (errorCode 0) from protocol errors. 2 tests in `http2-client.test.js` section 09.
* HTTP/2 session-level guards now configurable (#H7) — `maxSessionRejectedStreams` and `maxSessionInvalidFrames` are now read from `settings.json` `http2Options` (defaults 100 and 1000). Previously hardcoded. Bundle boilerplate `settings.server.json` documents all four `http2Options` keys. 4 new tests in `server.isaac.test.js`.
* `onCompleteCall` formalized as `lib/async` module (#M4) — the Promise adapter previously lived only as an implicit global in `helpers/path.js`. Now a proper library at `lib/async/src/main.js`, registered in `lib/index.js` as `lib.async`. Input validation throws `TypeError` for missing/invalid emitters. Global injection delegates to the lib module. 16 tests in `test/lib/async.test.js`.
### Fixed
* Fixed method-based routing on shared URLs — the router now continues scanning after a URL match with a wrong method instead of immediately throwing 405. GET `/notes` and POST `/notes` can coexist as separate routing rules; a 405 is only emitted after the full scan finds no method match. (`core/server.js`)
* Fixed crash in `fitsWithRequirements` when a URL param route has no `requirements` block — router now accepts any non-empty segment value and correctly binds it to `req.params` / `req[method]` instead of throwing `TypeError`. (`lib/routing/src/main.js`)
* Fixed `throwError(res, code, stringMsg)` silently overriding an explicit 3-digit HTTP status code with `res.status || 500` — `throwError(res, 404, 'Not found')` now correctly sends 404 instead of 500. (`core/controller/controller.js`)
* Fixed port scanner `opt.end` hardcoded window — `scan.js` previously used a fixed `start + 899` ceiling regardless of `limit`, so a `port:reset` scan requesting many ports could exhaust the window before finding them all. Window is now `Math.min(start + Math.max(899, limit + 99), maxEnd)`, giving at least 899 ports of headroom for small limits and scaling proportionally for large ones (e.g. `protocol:set` passes `protocols × schemes × envs × bundles`). Unit tests added in `test/unit/port-scan.test.js`.
* Fixed `query()` forwarding errors directly to `throwError()` on non-2xx upstream responses — the callback now receives the error object so controllers can implement graceful degradation instead of always surfacing a hard error. (#Q1)
* Fixed `throwError()` logging empty for plain-object errors from `query()` — `err.stack || err.message` was undefined; now serializes `errorObject.error` and captures the callsite stack when no stack is present. (#Q1)
* Fixed `controller.render-swig.js` error field priority — `statusCodes[status]` was evaluated first, burying the actual upstream error reason; `data.page.data.error` and `data.page.data.message` now take priority, `statusCodes[status]` is the fallback. Added `console.error` log before `throwError` so the upstream reason appears in bundle logs. (#Q1)
* Fixed `whisper()` error log showing `{key}` instead of `${key}` for missing dictionary keys.
* Fixed `bundle:start` (socket-server path) not initialising `ctx.envVars` — `gna.js defineDefault()` could not create GINA_* globals when the key was absent.
* Fixed `env.json` placeholder substitution — `${bundlesPath}`, `${homedir}`, `${cachePath}`, `${projectName}`, `${projectVersion}`, `${projectVersionMajor}` were missing from the `reps` dict in `CmdHelper`; whisper left them unresolved.
* Fixed `package.json` `"main"` field still pointing to `framework/v0.2.1-alpha.3/core/gna` after version bump — now `framework/v0.3.0-alpha.1/core/gna`.
* Fixed error page displaying `[object Object]` when upstream returned an error object instead of a string — `render-swig` now normalises `error` and `message` fields to strings before building the error response. (#Q2)

## 0.2.1-alpha.3 - 2026-03-29
Internal version bump — no user-facing changes. Repository URLs updated from Rhinostone to gina-io.

## 0.2.0 - 2026-03-29
### Added
* NODE_COMPILE_CACHE (#P1): set at startup in gna.js to ~/.gina/cache/v8 — Node.js >= 22.8 caches V8 bytecode to disk, reducing cold-start time by 30–60% on subsequent runs. No-op on older Node versions.
* JSON Schemas for routing.json, app.json, connectors.json, settings.json, app.crons.json — machine-readable schemas at schema/ in the package root. Scaffold templates now include "$schema" references to https://gina.io/schema/ for free editor validation and autocomplete in VS Code and any JSON Schema-aware IDE (#AI1)
* GitHub Discussions enabled — community Q&A and Show and Tell categories now available on the repo (#A10)
* Added V8 pointer compression detection at startup: sets GINA_V8_POINTER_COMPRESSED=true and logs heap limit when Node.js is built with --experimental-enable-pointer-compression (#P4)
* Added unit tests for #M2 entity._arguments buffer management — covers Promise path deletion and DISPATCH:BUFFER_CALLBACK deletion (3 tests, test/core/entity-arguments.test.js)
* Redis session store connector (CN1): ioredis-backed store with standalone, Cluster, and TLS support; configured via connectors.json — no credentials in application code
* SQLite session store connector (CN2 v1): node:sqlite-backed store (built-in since Node 22.5.0, zero npm deps) with WAL mode, prepared-statement caching, and background expiry cleanup; supports file-based and ':memory:' databases
* Added GitHub Actions CI pipeline — runs the full test suite (494+ tests) on Node.js 18, 20, and 22 on every push and PR to `develop` and `master` (#A1)
* Stable release now auto-tags, merges to master, and publishes the next alpha to npm
* SQLite ORM connector v2: entity wiring + SQL method generation from .sql files, Option-B Promise/.onComplete() pattern, connector.js (WAL, FK, synchronous open), 56 unit tests
* Added llms.txt — LLM-optimised framework reference for Cursor, Copilot, Claude.ai
* Added peerDependencies (all optional) for connector client libraries: couchbase, ioredis, mysql2, pg, mongodb, @scylladb/scylla-driver
* Added GitHub issue templates (bug_report, feature_request) and PR template
* SQLite state storage for ~/.gina/ config files (CN2 v3): lib/state.js StateStore routes all state-file writes through node:sqlite (DatabaseSync) for atomic writes and concurrent access safety; JSON sidecars kept for backwards-compat read paths
* Added WatcherService (lib/watcher): fs.watch-based file-change registry. Reads watchers.json bundle config and starts native file watchers on server startup. Exposes gna.watcher for framework-internal registration (#M6). 9 new tests in test/core/watcher.test.js.
* bundle:add preserves existing bundle source when the src/ directory already exists and rewrite is false — cloned starter repos are no longer overwritten
### Changed
* README: added 3-bullet why-Gina summary, feature highlights table, and Quick start section at the top; added MIT license and Node.js >= 18 badges; updated npm keywords to be search-relevant (http2, multi-bundle, event-driven, rest, api, couchbase) (#A9, #V3)
### Fixed
* Removed dead singleton infrastructure from SuperController (SuperController.instance, SuperController.initialized, init()/getInstance() singleton path); removed 4 no-op freeMemory([], false) calls; fixed throwError() HTML error path missing local.req/res/next = null after res.end() (#M1)
* Added deprecation warning to Couchbase SDK v2 connector: logs on every connection when sdk.version is 2 (EOL since 2021, removal planned for 0.4.0). Adds a separate fatal error when GINA_V8_POINTER_COMPRESSED=true, as v2 NAN bindings are incompatible with pointer-compressed Node.js builds.
* Fixed DISPATCH:BUFFER_CALLBACK path (util.promisify path in entity method wrappers): entity._arguments[trigger] was consumed but not deleted after use, allowing a buffered result from one request to persist and resolve all subsequent callers with stale data. Now deletes the buffer after consuming, matching the Promise path behaviour (#M2).
* Retired freeMemory() helper in controller pipeline — replaced all call sites with explicit local.req/res/next nulling so intent is visible at each response exit point (#M3)
* Fixed `/_gina/info` HTTP/2 endpoint returning `[object Object]` — `stream.end(infoHeaders)` was passing the headers object instead of the JSON string `infoStatus` (#H1)
* Added stream-closed guard (`stream.destroyed || stream.closed`) to `throwError()` HTML error response path — prevents unhandled error on HTTP/2 when a stream closes between error trigger and response write (#H2)
* Fixed infinite recursion in connector ping() when keepAlive is false (CB-BUG-4): replaced unconditional self.ping() recursive call with return in v2/v3/v4
* Removed redundant modelUtil.setConnection() call in connector v2/v3/v4 onConnect() (CB-PERF-3): first unconditional call before the existsSync guard was a no-op duplicate
* Fixed gina.onError() handler accumulation on Couchbase reconnect (CB-BUG-1): guarded registration with _errorHandlerRegistered flag so only one handler is registered per connector instance across reconnections (v2/v3/v4)
* Fixed session-store.v3 get() always returning session-not-found (CB-BUG-2): converted to async/await so Promise resolves before checks run — every session lookup was failing silently, forcing re-authentication on every request
* Fixed session-store.v3 set() silently discarding writes (CB-BUG-3): converted to async/await so upsert is awaited before calling fn() — writes appeared to succeed but were never confirmed
* Fixed CB-PERF-1 (300ms startup delay), CB-PERF-2 (ping reconnect drops callback), CB-QUAL-2 (eval() for @options parsing), CB-QUAL-3 (bulkInsert returns plain object instead of Promise) in Couchbase connector
* Fixed CB-QUAL-4, CB-LOW-1–5, CB-LOW-7: sanitised bulkInsert key in N1QL, fixed uuid implicit global, re-enabled strict mode in index.js, removed dead os.version import, removed duplicate use-strict in session-store.v4, added exponential backoff for Couchbase reconnects, removed dead commented-out blocks in connector files
* Fixed bundle:start hang: JSON.parse(process.argv[3]) in gna.start() threw SyntaxError in CLI mode where argv[3] is the project name (plain string), causing every bundle to crash silently and wait the full 60-second timeout. Added JSON format guard. Also added immediate crash detection in the start.js child watcher for non-zero exits during startup (#B9).
* Made engine.io a lazy require in server.isaac.js — loaded only when options.ioServer is configured, so bundles without WebSocket support no longer crash at startup if engine.io is absent
* Made ssl-checker a lazy require in server.js — loaded only inside verifyCertificate(), so bundles not using HTTPS cert verification no longer crash at startup if ssl-checker is absent
* Fixed Couchbase entity methods called via util.promisify without .bind(): instead of crashing with a cryptic 'Cannot set properties of undefined (setting _isRegisteredFromProto)', the framework now transparently recovers the entity singleton so the call succeeds
* Fixed EventEmitter listener leak on entity singleton when using util.promisify without .bind() (intermittent 500 errors under repeated requests on the same entity method)
* Boilerplate templates installed by view:add now render correctly — doubled html/ prefix removed from extends and include paths
* cache:stats now falls back through all assigned port/scheme combinations instead of trying only the first — fixes ECONNREFUSED on bundles that run HTTP/2 only
* Fixed entity method concurrency: concurrent callers on the same method now each receive their own result (FIFO queue dispatch replaces single-slot _callbacks; DISPATCH:PREEMPTIVE_BUFFER and consume paths queue-aware)
* All user-customisable settings (port, debug_port, mq_port, host_v4, hostname, logdir, tmpdir, rundir, log_level) now survive a shortVersion bump (e.g. 0.1 → 0.2) — previously they were silently reset to defaults because post_install pre-creates ~/.gina/<shortVersion>/settings.json from the template before init.js runs, making the migration block unreachable; fixed by detecting the unresolved template and triggering migration in that case too
### Security
* Added four missing HTTP/2 server security settings in `isaac.js`: `maxHeaderListSize: 65536` (HPACK bomb defense), `enablePush: false` (deprecated in browsers), `maxSessionRejectedStreams: 100` (RST/rapid reset flood defense — CVE-2019-9514, CVE-2023-44487), `maxSessionInvalidFrames: 1000` (CONTINUATION flood defense — CVE-2024-27316, CVE-2024-27983) (#H3)
* Fixed Couchbase restQuery() credential exposure and shell injection (CB-SEC-1, CB-SEC-2): replaced exec(curl...) with http.request(), credentials now sent via Authorization header, no shell involved
* Removed err.stack from HTTP 500 JSON response body in Couchbase connector (CB-QUAL-1): stack traces with filesystem paths now logged server-side only, never sent to clients

## 0.1.8 - 2026-03-27
### Added
* Added .github/dependabot.yml — weekly npm dependency updates for root package.json; vendored framework deps excluded
* Added _debugLog / _isDebugLog to server.isaac.js (LOG_LEVEL=debug|trace outputs gray-coded timestamps with [gina:isaac] group tag, matching gna.js and server.js)
### Changed
* package.json description updated to: Node.js MVC framework with built-in HTTP/2, multi-bundle architecture, and scope-based data isolation — no Express dependency
### Fixed
* Removed swig devDependency declarations (package.json.dist) from git — eliminated 6 Dependabot false-positive alerts (lodash, express, phantomjs are swig test-only deps, never installed or reachable at runtime)
* Popin overlay stuck active after form submit: data-gina-popin-loading attribute was set on XHR readyState 1/3 but never removed on readyState 4 (response complete), leaving the overlay blocking subsequent interactions. Cleared in xhr.onreadystatechange readyState==4, popinClose, and popinReset.
* Couchbase connector: @include directives sent raw to Couchbase in dev mode — the dev-mode SQL re-read refreshed the query body without re-expanding @include tokens, causing N1QL syntax errors. Dev-mode re-read now applies the same @include expansion as the startup parser.

## 0.1.8-alpha.1 - 2026-03-26
### Added
* Config variable interpolation now accepts \${variable} syntax in addition to {variable}. Both forms work in all config files (routing.json, app.json, settings.json, etc.). The {variable} syntax continues to work unchanged — migration to \${variable} is optional and will be documented in a future release.
* GINA_LOG_STDOUT=true — stdout-only mode for containerised deployments: logs emit as JSON lines (ts, level, group, msg), MQ transport disabled; compatible with kubectl logs, Fluentd, Datadog
* gina-init: stateless ~/.gina/ bootstrap for Docker/K8s containers — generates projects.json, ports.json, ports.reverse.json, and settings.json from env vars or a mounted JSON config file, with no gina CLI ceremony required
* critical: false option on this.query(): fire-and-forget HTTP/2 calls can now pass { critical: false } to swallow errors silently (log-only) instead of propagating to throwError and terminating the user-facing response (#H3)
* critical: false option on this.query(): fire-and-forget HTTP/2 calls can now pass { critical: false } to swallow errors silently (log-only) instead of propagating to throwError and terminating the user-facing response (#H3)
* HTTP/2 startup warmup: bundles can now declare server.warmup: ['https://upstream:port'] in their server config to pre-establish HTTP/2 sessions at startup. Warmup fires after bundle start, sends an initial PING to verify connectivity, then starts the 5s keepalive cycle — so the first real request reuses a live session instead of cold-connecting (#H5)
* Couchbase connector: _scope property on entities (mirrors _collection), $scope placeholder substitution in N1QL queries, _scope stored in bulkInsert records. scope read from connectors.json config, falls back to NODE_SCOPE env var
* Mockable service locator (#R2): getConfig() and getModel() now check for a __mock__ context override before hitting the real implementation. Test setup calls setContext('__mock__', { config: fn, model: fn }) — zero breaking changes, zero business logic changes. Lifts testability from E2E-only to unit-testable models and helpers
* EntitySuper now accepts optional `injected` 3rd constructor param — `{connector, config}` — for unit testing entities without a running database or framework server (#R3). getConnection() returns injected.connector when set. New this.getConfig() instance method routes through injected.config, falling back to the global getConfig()/__mock__ chain (#R2).
* SuperController.createTestInstance(deps) — static factory for controller unit testing (#R4). Creates a fresh instance with isolated local closure, wires mock req/res/next/options via setOptions(), marks it with _isTestInstance=true. Production singleton is never modified. Normalises missing conf structure so setOptions doesn't crash on minimal test deps.
* Auto-migrate main.json and settings.json on first startup of a new short version (e.g. 0.1 → 0.2, 0.5 → 1.0): copies all namespaced keys from the previous release, seeds port/hostname from the previous settings.json. Downgrade is free — old keys are never removed.
### Changed
* whisper() now requires \${variable} syntax — bare {variable} placeholders are no longer replaced (migration: update config files and templates to use \${variable})
* bundle:add now auto-detects system locale and timezone when scaffolding settings.json (LANG/LC_ALL → Intl → en_CM / UTC fallbacks)
* conf/settings.json locale fallback now auto-detected from GINA_CULTURE at bundle config load (preferedLanguages, region, dateFormat.short, 24HourTimeFormat)
* conf/settings.json locale defaults now resolved from built-in locale database: currency.code (ISO 4217) and preferedLanguages pulled from core/locales/dist/region/ by country code
* conf/settings.json locale defaults fully auto-detected: measurementUnits (imperial for US/LR/MM), temperature (fahrenheit for US/BS/KY/PW), firstDayOfWeek from Intl.Locale.getWeekInfo()
### Fixed
* Fixed dev-mode resource churn (#B3): DNS resolver and cache wrapper were recreated on every HTTP request because controller.js is re-required each time by the hot-reload mechanism. Both are now persisted on process.gina and reused across re-requires. Eliminates silent abandonment of in-flight DNS lookups and loss of cache-invalidation listeners between requests.
* Fixed W2 migration regression: resource template placeholders were written as \${} (empty variable) instead of \${variableName}, causing gina CLI to crash with RangeError on container startup. Also added mq_port validation in MQSpeaker to guard against malformed settings values.
* Fixed W2 migration regression: {variable} → ${variable} in bin/cli, bin/gina-container, core/config.js, entityFactory.js, framework/init.js, bundle/add.js, and resources/locals.json (#B4)
* Fixed remaining W2 regression: {gina}/{version} in templates test, {Model} in entityFactory boilerplate, {projectPath} in merge test; updated stale framework version import in merge.test.js (#B4)
* Fixed HTTPS bundle abort on startup: verifyCertificate failure during dev startup (e.g. DNS failure or self-signed cert in containers) was logged at emerg severity, triggering start.js abort detection even though the server was already listening. Downgraded to warn.
* Fixed rootDomain placeholder detection in config.js to match both `{rootDomain}` (old format) and `${rootDomain}` (new format) after the W2 interpolation migration.
* getConfig() no-arg now exposes conf.settings — settings.json content is accessible as conf.settings on the bundle conf object, consistent with conf.security
* Fixed Couchbase connector path resolution in model loader: conf.connectorsPath was never set, causing a MODULE_NOT_FOUND crash on first Couchbase connect.
* Fixed entity loader in Couchbase connector crashing with TypeError when an entities directory contains helper modules that export plain objects instead of constructor functions.
* HTTP/2 timeout messages now display in seconds when the value exceeds 1000ms (e.g. 30s instead of 30000ms)
* HTTP/2 client now retries on GOAWAY mid-stream premature close and on 502 upstream responses. Previously both failures surfaced immediately to callers with no retry, requiring application-level degraded-mode fallbacks. The 502 retry also adds a req.on('response') handler that captures the :status pseudo-header — previously unread, making nginx-level errors indistinguishable from JSON parse failures.
* renderJSON no longer throws ERR_HTTP2_INVALID_STREAM when the client disconnects (nginx timeout, browser navigation) before an async callback (Couchbase, HTTP/2 upstream) completes. The ServerHttp2Stream destroyed/closed state is now checked before stream.respond() and the request is silently dropped with a warning log.
* entity.js: guard _resolver before .then() callbacks — prevents uncaught TypeError when a Promise-returning method has no registered trigger in _triggers, which corrupted the event loop
* Fixed iso_short derivation in framework init — was using country code (cm) instead of language code (en) from culture en_CM
* Fixed ReferenceError crash on startup: defIsoShort and defDate were referenced outside their declaring function in init.js

## 0.1.7 - 2026-03-20
### Added
* Cache: sliding-window expiration and absolute ceiling (maxAge) for HTML and JSON response caches
* Added gina cache:stats <bundle> @<project> CLI command. Fetches and prints a grouped table of in-memory cache entries from a running bundle. Requires the bundle to be running.
* Cached routes now emit `Cache-Control` headers synchronised with the server-side TTL. A new optional `visibility` field (`"public" | "private"`, default `"private"`) in the per-route cache config controls the directive sent to browsers and CDNs. The hit path uses the remaining TTL so downstream caches do not over-serve stale content.
* Per-route outgoing query timeout: add "queryTimeout": <ms> to any routing.json rule and self.query() will use it as the request timeout budget when options.requestTimeout is not explicitly set. The hardcoded 10 s default still applies when neither is set.
### Changed
* Cache FS writes in the JSON renderer are now asynchronous (fs.promises.writeFile), avoiding event-loop blocking on cache misses.
* routing.json queryTimeout field now accepts human-readable strings ("30s", "500ms", "2m", "1h") in addition to integer milliseconds (30000). The value is normalised to ms at parse time so req.routing.queryTimeout is always a number.
* app.json proxy target field "timeout" renamed to "requestTimeout" — unambiguous name for the per-request outgoing timeout. Update any app.json proxy target that declares a "timeout" to use "requestTimeout" instead. self.query() call sites that passed options.timeout must also be updated to options.requestTimeout.
* self.query() options field "timeout" renamed to "requestTimeout" — consistent with app.json::proxy.<service>.requestTimeout. The internal pipeline (both HTTP/1 and HTTP/2 handlers) and the defaultOptions fallback now use requestTimeout throughout
* Entity methods and Couchbase N1QL methods now return a native Promise (Option B). await entity.method(args) works directly without util.promisify. .onComplete(cb) remains fully backward-compatible. Removes the _isRegisteredFromProto guard that was silently dropping queries when a callback-path call preceded a Promise-path call on the same entity.
### Fixed
* Sub-second TTL and maxAge values (e.g. ttl: 0.5) now work correctly in the cache. Previously, fractional seconds were truncated to zero, causing immediate eviction.
* Completed sub-second maxAge fix (#C1): both renderers were pre-truncating maxAge with ~~ before passing it to lib/cache, bypassing the Math.round fix. Removed ~~ from render-swig.js and render-json.js. Also fixed Cache-Status header calculation in server.isaac.js.
* Cache keys now include the bundle name ("static:{bundle}:{url}" and "data:{bundle}:{url}") to prevent silent cache collisions when two bundles serve the same URL path on the same server instance.
* Cache entries are now capped via LRU eviction. Cache(options) accepts a `maxEntries` option (default 0 = unlimited); when reached, the least-recently-accessed entry is evicted before each insert. The shared server cache defaults to 1000 entries, configurable via `server.cache.maxEntries` in env.json. The routing cache is capped at 5000 entries (FIFO).
* Fixed handler files returning 404 when a catch-all root statics mapping (empty key) was defined: staticResources is now sorted by descending path length so specific prefixes like /handlers/ are matched before / in the prefix-regex loop
* CORS preflight (OPTIONS) requests are now handled before routing: the server responds with HTTP 204 and the correct Access-Control-Allow-Origin header immediately, instead of forwarding to the controller action which returned an error without CORS headers. CORS response headers are now also included on all HTTP/2 JSON responses.
* self.query() queryTimeout fallback now fires correctly: moved the req.routing.queryTimeout check to before the merge with defaultOptions, which was silently overwriting timeout with the "10s" framework default before the check could run
* controller.render-swig.js: isRenderingCustomError now detected from both userData and localOptions (set by renderCustomError() in controller.js). Adds null-guard on userData. localOptions is now resolved before the flag check.
### Security
* Removed dead framework version v0.1.1-alpha.1 from the repository. Its package.json declared sanitize-html ^2.5.0 (CVE-2021-26539, CVE-2021-26540 — XSS, high severity) and busboy ^0.2.14 (dicer ReDoS). Neither version nor its dependencies were in use.

## 0.1.6 - 2026-03-08
### Added
* Added JSDoc across core/controller/, core/model/, and helpers/: constructor @class/@constructor/@extends, @inner on private helpers, complete @param/@returns on all public methods, @global on the _ path helper, @module on controller/index.js
* Added JSDoc to lib/ runtime libraries: @module on all 13 files; @class/@constructor on Cache, Collection, ConfigUtil, Domain, Inherits, Merge, ModelUtil, Proc, SessionStore, Shell, Url, Validator; complete @param/@returns on all public methods; fixed @contructor typos and empty @returns stubs
* Added JSDoc to all lib/cmd/ command handlers (63 files) covering bundle, env, framework, minion, port, project, protocol, scope, view groups plus CmdHelper and the cmd dispatcher
* Added JSDoc to core/config.js, core/gna.js, core/server.js, core/server.isaac.js, and core/server.express.js
* post_publish now auto-increments the alpha version, renames the framework directory, updates ~/.gina config, and commits after each npm publish
* Added unit tests for V8 arm64 regression (const vs var object rest), routing cleanup, _isDebugLog gate, _debugLog format, and ANSI color codes in startup debug traces
* Added gina-container foreground bundle launcher for Docker/K8s containers (bin/gina-container)
### Changed
* Replaced synchronous fs I/O in render-swig.js (readFileSync, openSync/writeSync/closeSync), server.isaac.js (execSync for brotli/gzip), and config.js (readdirSync) with async equivalents (fs.promises, child_process.exec)
* Extracted try-catch config resolution block from Router::route() into module-level resolveRouteConfig() for V8 JIT optimization (#P25)
* Replaced string += in setOptions() routing param loop with Array.push()+join() for V8 optimization (#P26)
### Fixed
* Fixed HTTP/2 client requests crashing on retry when a request body was present, and corrected misleading undefined method/path in HTTP/2 stream error logs.
* Fixed HTTP/2 session cache growing without bound; sessions are now capped at 50 with FIFO eviction and properly removed on error, close, and goaway events
* Fixed query#complete listener accumulation: removeAllListeners() is now called before each once() registration in both HTTP/1 and HTTP/2 onComplete paths
* Fixed shared module-level EventEmitter in path helper: mkdir, cp, mv and rm now each create a per-call emitter, eliminating orphaned listener accumulation and cross-call event interference
* Fixed _maxListeners growing unbounded in entity.js: capped at 100 via Math.min to ensure Node's memory leak warning still fires if trigger accumulation goes beyond the realistic method count ceiling
* Fixed undrained http.IncomingMessage on 4xx in downloadFromURL: res.destroy() is now called before rejecting the Promise, releasing the TCP connection immediately instead of waiting for remote timeout
* Fixed HTTP/2 response chunk string concatenation: chunks are now collected as Buffers and assembled with Buffer.concat() in the end handler, halving peak memory usage for large responses
* Fixed HTTP/1 retry timer accumulation: capped maxRetry at 10 via Math.min to bound outstanding setTimeout closures under sustained connection failure regardless of user config
* Fixed req/res held in controller closure: all response-terminal exit paths in controller.js, controller.render-json.js, and controller.render-swig.js now explicitly null local.req/res/next immediately after the response is sent
* Fixed SuperController.instance._options written on every request: removed write in getInstance() and setOptions() — _options was never read (all reads go through per-request local.options/self._options), making the write a silent corruption trap for future code
* Fixed a silent ENOENT 500 on requests when the swig layout cache directory could not be created (e.g. permission denied). The renderer now clears the cache target, logs a warning on every affected request, and falls back to the original configured layout path so the page renders normally. The swig compiled-template cache and response cache are also suppressed while the failure persists, ensuring the warning remains visible until the underlying issue is resolved.
* prepare_version no longer attempts a stale recovery or a no-op rename when the working version is already in sync with the published one
* Fixed invalid JSDoc tags (@package, bare @returns) in lib/inherits and lib/logger that caused jsdoc-to-markdown parse errors
* Fixed ReferenceError: isLocalScope is not defined in SuperController.setOptions (controller.js:413) — bare call replaced with self.isLocalScope()
* Fixed post_install.js MIDDLEWARE update: .toString missing () caused comparison to always be true; added missing done() call in the already-in-sync branch to prevent hang
* Fixed HTTP/2 client stream premature close silently hanging requests: req.on('close') now emits an error when the stream closes before 'end' fires (GOAWAY race, network reset, or session timeout)
* Added stream-level timeout to HTTP/2 client requests (mirrors HTTP/1 path): streams that receive no events now time out instead of hanging indefinitely
* Added TCP keepalive and setNoDelay on HTTP/2 session socket to prevent OrbStack (ARM64) from silently dropping idle inter-container connections
* HTTP/2 stream timeout now auto-retries with a fresh session instead of surfacing a 500 error — transparent recovery when OrbStack silently drops idle inter-container connections
* Fixed null lib.generator crash in post_publish.js by initializing lib inside begin() to avoid circular dependency at module load time
* Fixed bundle crash on startup when verifyCertificate DNS lookup fails inside containers (unhandled rejection from async setTimeout callback now logs at emerg level instead of aborting the process; added 5 s timeout to ssl-checker to prevent indefinite hangs)
* Fixed credentials.ca path not expanding ~/ before fs.readFileSync in verifyCertificate (server.js) and http2Options (server.isaac.js); credentials paths use ~/ which fs.readFileSync cannot resolve — now uses _() for tilde expansion
* Fixed logger silently swallowing all log output when LOG_LEVEL is set to a level name with no hierarchy entry (e.g. 'notice', 'alert', 'crit'): init now falls back to 'info' with a warning, matching the existing setLevel() guard
* Fixed bundle crash when HTTP/2 client abruptly disconnects (ECONNRESET): added session error handler in server.isaac.js and restored ECONNRESET guard in proc.js uncaughtException handler
* Fixed bundle being SIGKILL'd by start.js stdout watchdog when a runtime uncaughtException triggered an emerg log — the emerg-detection guard in child.stdout.on('data') is now skipped once the bundle is running, letting proc.js handle the lifecycle cleanly and allowing the actual error to reach the logs
* Replaced per-request regex /^(\.|\/|\\)/.test(file) with charAt(0) checks in render-swig.js and render-v1.js
* Added bundle startup integration test validating the async render pipeline (P28-P31) works end-to-end; test skips gracefully when gina socket is not running
* Reverted async readdirSync conversion in config.js (loadWithTemplate, loadBundleConfig) — async I/O in these functions breaks the synchronous Config initialisation contract, causing bundle startup crashes with TypeError: Cannot read properties of undefined (reading 'bundlesConfiguration')
* Fixed {{ page.environment.* }} placeholders not substituted in ginaLoader (gina.onload.min.js) — ginaLoader is injected after Swig compilation so whisper() must resolve them; dic was missing page.environment.key flattened entries in render-swig.js and render-v1.js
* Fixed %s placeholders not substituted in session-store v3/v4 debug log calls (gina logger does not support printf-style formatting)
* Eliminated [DOMAIN] PSL Loaded ×2 noise per request in dev mode: removed unused Domain import and instantiation from controller.js (dead code), and removed redundant direct pre-load of controller.js from refreshCoreDependencies() in router.js
* Added graceful HTTP shutdown on SIGTERM: server.close() drains in-flight requests before exit, with configurable timeout (GINA_SHUTDOWN_TIMEOUT env var, default 10s) and closeIdleConnections() for keep-alive connections (Node 18.2+ http.Server)
* Fixed per-request "Logger instance already exists: reusing it ;)" debug noise in dev mode. Root cause: refreshCore() (server.isaac.js) deleted and re-required lib/index.js on every HTTP request, which re-ran Lib() and called _require('./logger'), evicting and re-loading the logger singleton on every request. Logger is persisted via getContext('loggerInstance') and does not benefit from hot-reload. Changed lib/index.js to use plain require('./logger') instead of _require('./logger').
### Security
* Fixed directory traversal vulnerability (CVE-2023-25345) in the swig template engine: a malicious template using `{% extends %}` or a path starting with `./`, `/`, or `\` could read arbitrary files outside the template root.
* Removed dead framework version v0.1.1-alpha.1 from the repository. Its package.json declared sanitize-html ^2.5.0 (CVE-2021-26539, CVE-2021-26540 — XSS, high severity) and busboy ^0.2.14 (dicer ReDoS). Neither version nor its dependencies were in use.
* Removed internal development files from the npm package. The published tarball no longer includes .env, CLAUDE.md, .claude/ (internal notes), .changes/ (changie entries), .changie.yaml, services/ (WIP bundles), vendor benchmark and CI files, library example and test files, locale build sources, and stray backup files. Previously .npmignore did not cover these paths.
