diff --git a/README.md b/README.md index 4e57996d9..bd3109c68 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ The managed cloud runs **this same engine**. Cloud adds operations and product c | --- | --- | | Docs | [docs.remember.dev](https://docs.remember.dev) | | Managed product | [remember.dev](https://remember.dev) | -| Release | [v0.13.0](https://github.com/writeitai/remember-stack/releases/tag/v0.13.0) | +| Release | [v0.16.0](https://github.com/writeitai/remember-stack/releases/tag/v0.16.0) | --- diff --git a/RELEASING.md b/RELEASING.md index ca4a54f75..0f0325b6a 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,9 +1,10 @@ # Releasing RememberStack The `Release` workflow publishes one version to PyPI and GHCR, then creates a GitHub release -containing the Python distributions, the same version-pinned `compose.yaml`, and the example -environment as `default.env.example` (GitHub's public asset name for the source -`.env.example`). It accepts only tags exactly matching `vMAJOR.MINOR.PATCH`. +containing the Python distributions, the same version-pinned `compose.yaml`, the generated +`openapi.json`, and the example environment as `default.env.example` (GitHub's public asset +name for the source `.env.example`). It accepts only tags exactly matching +`vMAJOR.MINOR.PATCH`. ## One-time owner setup @@ -105,7 +106,15 @@ Run these checks from a clean machine or temporary directory: uvx --from rememberstack==0.2.0 remember --version docker pull ghcr.io/writeitai/remember-stack:0.2.0 gh release download v0.2.0 --repo writeitai/remember-stack \ - --pattern compose.yaml --pattern default.env.example + --pattern compose.yaml --pattern default.env.example --pattern openapi.json +found=$(jq -r '.info.version' openapi.json) || { + echo "cannot read openapi.json" >&2 + exit 1 +} +[ "$found" = "0.2.0" ] || { + echo "openapi.json is version $found, expected 0.2.0" >&2 + exit 1 +} cp default.env.example .env docker compose --env-file .env up --no-build --pull always --detach --wait curl --fail http://localhost:8000/healthz diff --git a/compose.yaml b/compose.yaml index 89626413e..ecdc9e26e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -9,7 +9,7 @@ x-app: &app # Empty when the caller does not supply it; released images get the tag's # commit from CI. REMEMBERSTACK_BUILD_REVISION: ${REMEMBERSTACK_BUILD_REVISION:-} - image: ghcr.io/writeitai/remember-stack:0.15.0 + image: ghcr.io/writeitai/remember-stack:0.16.0 environment: REMEMBERSTACK_DATABASE_URL: postgresql+psycopg://${REMEMBERSTACK_POSTGRES_USER}:${REMEMBERSTACK_POSTGRES_PASSWORD}@postgres:5432/${REMEMBERSTACK_POSTGRES_DB} REMEMBERSTACK_MINIO_ENDPOINT_URL: http://minio:9000 diff --git a/openapi.json b/openapi.json index 7fd0575a5..4690c7b67 100644 --- a/openapi.json +++ b/openapi.json @@ -3260,7 +3260,7 @@ }, "info": { "title": "RememberStack query API", - "version": "0.15.0" + "version": "0.16.0" }, "openapi": "3.1.0", "paths": { diff --git a/pyproject.toml b/pyproject.toml index afc8ee143..ce2854a3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "rememberstack" -version = "0.15.0" +version = "0.16.0" description = "Open memory infrastructure for AI agents." readme = "README.md" requires-python = ">=3.12" diff --git a/scripts/check_release_contract.py b/scripts/check_release_contract.py index b818c367b..03159cee7 100644 --- a/scripts/check_release_contract.py +++ b/scripts/check_release_contract.py @@ -21,6 +21,7 @@ def main() -> None: version = _package_version(root=root) _validate_semver(version=version) _validate_compose_pin(root=root, version=version) + _validate_release_docs(root=root, version=version) _validate_postgres_release(root=root) if arguments.tag is not None: _validate_tag(tag=arguments.tag, version=version) @@ -78,6 +79,39 @@ def _validate_tag(*, tag: str, version: str) -> None: raise ValueError(f"release tag must be {expected!r}, found {tag!r}") +def _validate_release_docs(*, root: Path, version: str) -> None: + """Keep public version claims on the same coordinate as the artifacts.""" + image = f"ghcr.io/writeitai/remember-stack:{version}" + markers = { + Path("README.md"): ( + f"[v{version}](https://github.com/writeitai/remember-stack/releases/tag/v{version})", + ), + Path("website/src/app/docs/getting-started/page.mdx"): (image,), + Path("website/src/app/docs/deployment/page.mdx"): ( + f"`v{version}` release", + image, + ), + Path("website/src/app/docs/reference/cli/page.mdx"): ( + f"# RememberStack {version}", + ), + Path("website/src/app/docs/reference/api/page.mdx"): ( + f"Release v{version} and later", + f"releases/download/v{version}/openapi.json", + ), + Path("website/src/app/docs/project-status/page.mdx"): ( + f"releases/tag/v{version}", + f"rememberstack/{version}/", + ), + } + for relative_path, expected_markers in markers.items(): + document = (root / relative_path).read_text(encoding="utf-8") + for marker in expected_markers: + if marker not in document: + raise ValueError( + f"{relative_path} must contain release coordinate {marker!r}" + ) + + def _validate_postgres_release(*, root: Path) -> None: """Bind Compose to the multi-architecture immutable image publisher.""" dockerfile = (root / "Dockerfile.postgres").read_text(encoding="utf-8") diff --git a/src/tests/packaging/test_release_contract.py b/src/tests/packaging/test_release_contract.py index 703e3d5bc..07928b358 100644 --- a/src/tests/packaging/test_release_contract.py +++ b/src/tests/packaging/test_release_contract.py @@ -5,6 +5,18 @@ import sys import tomllib +import pytest +from scripts.check_release_contract import _validate_release_docs + +_RELEASE_DOCS = ( + Path("README.md"), + Path("website/src/app/docs/getting-started/page.mdx"), + Path("website/src/app/docs/deployment/page.mdx"), + Path("website/src/app/docs/reference/cli/page.mdx"), + Path("website/src/app/docs/reference/api/page.mdx"), + Path("website/src/app/docs/project-status/page.mdx"), +) + def test_release_contract_matches_package_compose_and_tag() -> None: """Accept the current package version, Compose image, and matching tag.""" @@ -48,6 +60,33 @@ def test_release_contract_rejects_a_mismatched_tag() -> None: assert f"release tag must be 'v{version}', found '{invalid_tag}'" in result.stderr +def test_release_contract_rejects_a_stale_document_coordinate(tmp_path: Path) -> None: + """Reject a public document that advertises a different release.""" + root = Path(__file__).resolve().parents[3] + version = _project_version(root=root) + for relative_path in _RELEASE_DOCS: + destination = tmp_path / relative_path + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text( + (root / relative_path).read_text(encoding="utf-8"), encoding="utf-8" + ) + + cli_reference = tmp_path / "website/src/app/docs/reference/cli/page.mdx" + cli_reference.write_text( + cli_reference.read_text(encoding="utf-8").replace( + f"# RememberStack {version}", "# RememberStack 0.0.0", 1 + ), + encoding="utf-8", + ) + + with pytest.raises(ValueError) as error: + _validate_release_docs(root=tmp_path, version=version) + assert str(error.value) == ( + "website/src/app/docs/reference/cli/page.mdx must contain release " + f"coordinate '# RememberStack {version}'" + ) + + def _project_version(*, root: Path) -> str: """Read the package version independently from the release checker process.""" with (root / "pyproject.toml").open("rb") as pyproject: diff --git a/uv.lock b/uv.lock index 8b65baf04..8bad99cad 100644 --- a/uv.lock +++ b/uv.lock @@ -1693,7 +1693,7 @@ wheels = [ [[package]] name = "rememberstack" -version = "0.15.0" +version = "0.16.0" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/website/src/app/docs/deployment/page.mdx b/website/src/app/docs/deployment/page.mdx index e96ccdc4b..613fbe7f0 100644 --- a/website/src/app/docs/deployment/page.mdx +++ b/website/src/app/docs/deployment/page.mdx @@ -48,9 +48,9 @@ Pre-release volumes created by the older PostgreSQL image are not migrated. For this disposable quickstart, remove the old stack with `docker compose down --volumes` before starting a different PostgreSQL 19 prerelease checkout. -`--build` deliberately tests the checked-out source. The public `v0.13.0` release +`--build` deliberately tests the checked-out source. The public `v0.16.0` release also ships this Compose file pinned to -`ghcr.io/writeitai/remember-stack:0.13.0`; release consumers use +`ghcr.io/writeitai/remember-stack:0.16.0`; release consumers use `--no-build --pull always` so Compose cannot fall back to a local source build. Do **not** put cost export on the public query port. When you want HTTP export, diff --git a/website/src/app/docs/getting-started/page.mdx b/website/src/app/docs/getting-started/page.mdx index e8c0325fa..840389cf0 100644 --- a/website/src/app/docs/getting-started/page.mdx +++ b/website/src/app/docs/getting-started/page.mdx @@ -38,7 +38,7 @@ cp .env.example .env docker compose up --build --detach --wait ``` -The Compose file can also pull the public `ghcr.io/writeitai/remember-stack:0.13.0` image. Use `--build` when you want to run local source; use `--no-build --pull always` when you want the released image. +The Compose file can also pull the public `ghcr.io/writeitai/remember-stack:0.16.0` image. Use `--build` when you want to run local source; use `--no-build --pull always` when you want the released image. Health: diff --git a/website/src/app/docs/project-status/page.mdx b/website/src/app/docs/project-status/page.mdx index bc0fe3e37..6fa6de4e4 100644 --- a/website/src/app/docs/project-status/page.mdx +++ b/website/src/app/docs/project-status/page.mdx @@ -6,7 +6,7 @@ export const metadata = { # Project Status -Current public release: [`v0.15.0`](https://github.com/writeitai/remember-stack/releases/tag/v0.15.0) on [PyPI](https://pypi.org/project/rememberstack/0.15.0/) and [GHCR](https://github.com/writeitai/remember-stack/pkgs/container/remember-stack). +Current public release: [`v0.16.0`](https://github.com/writeitai/remember-stack/releases/tag/v0.16.0) on [PyPI](https://pypi.org/project/rememberstack/0.16.0/) and [GHCR](https://github.com/writeitai/remember-stack/pkgs/container/remember-stack). ## What ships today @@ -156,14 +156,49 @@ Current public release: [`v0.15.0`](https://github.com/writeitai/remember-stack/ The resolver generation moves to `resolver-2026.08g`, so evaluation curves measured under `08f` are not comparable and a fresh run is required. -## What landed after v0.15.0 (on `main`) +## What landed after v0.15.0 (in v0.16.0) + +**Dated events no longer collapse merely because their words match.** The +observation adjudicator now uses each claim's resolved world-time window. +Events with disjoint dates stay distinct unless a date-aware verdict identifies +one occurrence with disputed dates; a dated event also cannot serve as +`evidence` for an undated summary. Recurring same-shaped events therefore +survive as separate facts, and ingestion provenance changes +([#360](https://github.com/writeitai/remember-stack/pull/360)). + +**Claim windows have one canonical half-open meaning.** Day, month, quarter, +and year precision cover their complete calendar unit; instants remain +non-empty points; open and unknown bounds stay explicit. `claims_as_of` now +finds a day-precision claim from an intraday or point query, and observation +adjudication uses the same bounds +([#372](https://github.com/writeitai/remember-stack/pull/372)). + +**Operators:** migration `p9_26_0047` adds the canonical-bound SQL functions +and the partial expression index used by `claims_as_of`; claim storage is +unchanged. Observation generations roll, so rebuilding changes stored +ingestion provenance, and `claims_as_of` results change under the corrected +window semantics. + +**A release now carries the API contract beside the binaries.** The generated +`openapi.json` is attached to the GitHub release, so client generation can pin +the exact schema served by the installed engine rather than following `main` +([#358](https://github.com/writeitai/remember-stack/pull/358)). + +**Browser uploads have their own narrow credential.** The signed perimeter now +has `read`, `ingest`, and `write` scopes. `ingest` reaches only `POST /ingest`: +it cannot search, list memory, run operations, or create a connector that keeps +pulling after the browser leaves. Full `write` still reaches ingest. A narrow +credential also cannot assert immutable principal attribution +([#363](https://github.com/writeitai/remember-stack/pull/363), +[#370](https://github.com/writeitai/remember-stack/pull/370)). **Managed login uses the deployment hostname it receives.** When the token host advertises a live data-plane hostname, `remember login` derives the query API URL, so managed users need only `--token-host`. An explicit `--api-url` still wins for self-hosted and local deployments. A missing hostname asks for that flag, while a hostname that is not live prints the name and asks the user -to retry; neither refusal replaces an existing credential. +to retry; neither refusal replaces an existing credential +([#371](https://github.com/writeitai/remember-stack/pull/371)). ## What landed after v0.14.0 (in v0.15.0) @@ -246,11 +281,12 @@ examined, and the failure looks like the deployment being down. v16's read surface, answer/judge seats, budgets, and content-before-`Unknown` guard. -## Document-local exact T0 (current release behavior in v0.13.0) +## Document-local exact T0 (current release behavior) -- Resolver `resolver-2026.08f` can replay an exact canonical-name match at T0 - only inside the same document and only after a T4 match established one - unambiguous live binding. Fuzzy T1/T2 signals remain candidate-only. +- Resolver `resolver-2026.08g` can replay an exact canonical-name match at T0 + (a behavior introduced in `08f`) only inside the same document and only after + a T4 match established one unambiguous live binding. Fuzzy T1/T2 signals + remain candidate-only. - The derived binding projection is generation-gated as `document-t0-v1`, rebuilt before it is enabled for existing deployments, and cleared by normal lineage deletion and hard forget. diff --git a/website/src/app/docs/reference/api/page.mdx b/website/src/app/docs/reference/api/page.mdx index 1cc9c94b2..95d29e829 100644 --- a/website/src/app/docs/reference/api/page.mdx +++ b/website/src/app/docs/reference/api/page.mdx @@ -59,9 +59,8 @@ the routes a shipped self-host deployment serves — not the cost-export listene below, which is a separate process, nor connector management, which no shipped profile composes. The document is [`openapi.json`](https://github.com/writeitai/remember-stack/blob/main/openapi.json) -at the root of the repository, and releases published from now on attach it as -a release asset of the same name. Releases up to and including v0.15.0 predate -it, so check a release's asset list before pinning one to generate from — the +at the root of the repository. Release v0.16.0 and later attach it as a release +asset of the same name; releases up to and including v0.15.0 predate it. The repository copy always describes the current `main`. A deployment does **not** serve the schema itself. `GET /openapi.json` is not a @@ -75,14 +74,13 @@ nowhere else. Generate a typed client the usual way for your language, for example: ```bash -# From the repository, describing current main: +# Pinned to the version you actually run (available from v0.16.0): curl -fsSL -o openapi.json \ - https://raw.githubusercontent.com/writeitai/remember-stack/main/openapi.json + https://github.com/writeitai/remember-stack/releases/download/v0.16.0/openapi.json -# Or, pinned to the version you actually run — check the release's asset -# list first, since releases up to v0.15.0 do not carry it: -# curl -fsSL -o openapi.json \ -# https://github.com/writeitai/remember-stack/releases/download//openapi.json +# Or, from the repository, describing current main: +curl -fsSL -o openapi.json \ + https://raw.githubusercontent.com/writeitai/remember-stack/main/openapi.json # TypeScript npx openapi-typescript openapi.json -o src/engine-schema.ts diff --git a/website/src/app/docs/reference/cli/page.mdx b/website/src/app/docs/reference/cli/page.mdx index 70394edf0..9ea8d887a 100644 --- a/website/src/app/docs/reference/cli/page.mdx +++ b/website/src/app/docs/reference/cli/page.mdx @@ -32,7 +32,7 @@ in the deployment. One deployment is one trust domain. ```bash remember --version -# RememberStack 0.15.0 +# RememberStack 0.16.0 ``` ## `remember login` / `remember logout`