diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f52aad0..19b64c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,7 @@ name: Latch regression checks on: push: branches: [main] + tags: ['v*'] pull_request: workflow_dispatch: permissions: @@ -57,7 +58,7 @@ jobs: path: reports/ publish: needs: handler-and-unit - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest timeout-minutes: 10 permissions: diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f016d5..34e660b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.0.1 + +- Keep project-file reads tied to the checked descriptor and reject a swapped path before generating or applying an integration. +- Keep CI handler requests on the fixed loopback test server even when example fixture data supplies another URL. +- Verify the exact release tag before publishing the six matching package archives. + ## 1.0.0 2026-10-06 diff --git a/README.md b/README.md index 698361d..0a423a7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ The [September compatibility record](docs/compatibility-2026-09-08.md), [Septemb The local CLI requires **Node 22+** and **pnpm 10.17.1+**. Integration targets an existing React, TypeScript, and Vite application. The [installation guide](docs/installation.md) lists tested versions and browser setup. -Version **1.0.0** uses six prebuilt package tarballs with checksums in the [GitHub release](https://github.com/agammann/latch/releases/tag/v1.0.0). The `@latch-local/*` names are local distribution identifiers; packages have not been published to a registry. The public Studio runs on OpenAI Sites. This repository contains the CLI, runtime, local console, examples, and release evidence; the Studio website source is maintained separately. Studio version 5 runtime downloads were verified against the six published v1.0.0 packages on October 7, 2026 (UTC); see the [delivered-release check](docs/studio.md#delivered-v1-release-check-october-7-2026-utc). The [v1 stability contract](docs/stability.md) covers the CLI, typed runtime contracts and React/Vite integration. +Version **1.0.1** uses six prebuilt package tarballs with checksums in the [GitHub release](https://github.com/agammann/latch/releases/tag/v1.0.1). The `@latch-local/*` names are local distribution identifiers; packages have not been published to a registry. The public Studio runs on OpenAI Sites. This repository contains the CLI, runtime, local console, examples, and release evidence; the Studio website source is maintained separately. Studio version 5 runtime downloads were verified against the six published v1.0.0 packages on October 7, 2026 (UTC); see the [delivered-release check](docs/studio.md#delivered-v1-release-check-october-7-2026-utc). The [v1 stability contract](docs/stability.md) covers the CLI, typed runtime contracts and React/Vite integration. ## Documentation and project status diff --git a/docs/installation.md b/docs/installation.md index 8276f5a..fad1037 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -14,10 +14,10 @@ The generator compiles against your `tsconfig.json`. The tested setup uses TypeS Choose one source: -1. **From GitHub:** download all six `latch-local-*-1.0.0.tgz` assets and `SHA256SUMS` from the [v1.0.0 release](https://github.com/agammann/latch/releases/tag/v1.0.0). Verify each tarball against SHA256SUMS before installing it. On PowerShell, use `Get-FileHash -Algorithm SHA256`; on Linux, run `sha256sum -c SHA256SUMS` in the download directory. +1. **From GitHub:** download all six `latch-local-*-1.0.1.tgz` assets and `SHA256SUMS` from the [v1.0.1 release](https://github.com/agammann/latch/releases/tag/v1.0.1). Verify each tarball against SHA256SUMS before installing it. On PowerShell, use `Get-FileHash -Algorithm SHA256`; on Linux, run `sha256sum -c SHA256SUMS` in the download directory. 2. **From this repository:** follow the checkout and dependency steps in the [example quickstart](quickstart.md), then run `pnpm build` and `pnpm pack:local` from the Latch repository root. The tarballs are written to `artifacts/`. -Studio is maintained separately. Its runtime download is not established as the v1.0.0 package set; obtain v1 packages through GitHub or a v1 source checkout. Studio integration configurations use schema 1 and must still pass your installed CLI checks. +Studio is maintained separately. Its runtime download is not established as the v1.0.1 package set; obtain v1 packages through GitHub or a v1 source checkout. Studio integration configurations use schema 1 and must still pass your installed CLI checks. In your application, create `vendor/latch/` and copy all six tarballs into it. Keep the `.tgz` files intact; pnpm installs them directly. The following steps assume this directory structure: @@ -28,12 +28,12 @@ your-app/ tsconfig.json src/ vendor/latch/ - latch-local-contracts-1.0.0.tgz - latch-local-browser-1.0.0.tgz - latch-local-runtime-1.0.0.tgz - latch-local-react-1.0.0.tgz - latch-local-test-1.0.0.tgz - latch-local-cli-1.0.0.tgz + latch-local-contracts-1.0.1.tgz + latch-local-browser-1.0.1.tgz + latch-local-runtime-1.0.1.tgz + latch-local-react-1.0.1.tgz + latch-local-test-1.0.1.tgz + latch-local-cli-1.0.1.tgz ``` For a pnpm workspace, put the overrides in the workspace root's `pnpm-workspace.yaml` and resolve their paths relative to that root. Run package installation commands in the application package. The example above assumes a single application root. @@ -44,12 +44,12 @@ Add these entries to your application's `pnpm-workspace.yaml`. If the file exist ```yaml overrides: - '@latch-local/contracts': file:./vendor/latch/latch-local-contracts-1.0.0.tgz - '@latch-local/browser': file:./vendor/latch/latch-local-browser-1.0.0.tgz - '@latch-local/runtime': file:./vendor/latch/latch-local-runtime-1.0.0.tgz - '@latch-local/react': file:./vendor/latch/latch-local-react-1.0.0.tgz - '@latch-local/test': file:./vendor/latch/latch-local-test-1.0.0.tgz - '@latch-local/cli': file:./vendor/latch/latch-local-cli-1.0.0.tgz + '@latch-local/contracts': file:./vendor/latch/latch-local-contracts-1.0.1.tgz + '@latch-local/browser': file:./vendor/latch/latch-local-browser-1.0.1.tgz + '@latch-local/runtime': file:./vendor/latch/latch-local-runtime-1.0.1.tgz + '@latch-local/react': file:./vendor/latch/latch-local-react-1.0.1.tgz + '@latch-local/test': file:./vendor/latch/latch-local-test-1.0.1.tgz + '@latch-local/cli': file:./vendor/latch/latch-local-cli-1.0.1.tgz ``` All six overrides are required because the packages depend on one another and are not published to a registry. Do not put these overrides only in `package.json`; pnpm 11 uses the workspace YAML settings. See the [pnpm settings reference](https://pnpm.io/settings). @@ -57,12 +57,12 @@ All six overrides are required because the packages depend on one another and ar Now install the browser dependencies and developer tools: ```sh -pnpm add --ignore-scripts ./vendor/latch/latch-local-contracts-1.0.0.tgz ./vendor/latch/latch-local-browser-1.0.0.tgz ./vendor/latch/latch-local-runtime-1.0.0.tgz ./vendor/latch/latch-local-react-1.0.0.tgz -pnpm add -D --ignore-scripts ./vendor/latch/latch-local-test-1.0.0.tgz ./vendor/latch/latch-local-cli-1.0.0.tgz +pnpm add --ignore-scripts ./vendor/latch/latch-local-contracts-1.0.1.tgz ./vendor/latch/latch-local-browser-1.0.1.tgz ./vendor/latch/latch-local-runtime-1.0.1.tgz ./vendor/latch/latch-local-react-1.0.1.tgz +pnpm add -D --ignore-scripts ./vendor/latch/latch-local-test-1.0.1.tgz ./vendor/latch/latch-local-cli-1.0.1.tgz pnpm exec latch --version ``` -The version command should print `1.0.0`. The Latch tarballs are prebuilt and need no installation scripts. These commands skip dependency lifecycle scripts for this installation; keep your existing application's build dependencies configured separately. This also avoids pnpm 11 stopping on an unapproved dependency build script. Retain the tarballs at their configured paths for future installs. Registry lookups for `@latch-local/*` usually mean an override is missing or points at the wrong directory. +The version command should print `1.0.1`. The Latch tarballs are prebuilt and need no installation scripts. These commands skip dependency lifecycle scripts for this installation; keep your existing application's build dependencies configured separately. This also avoids pnpm 11 stopping on an unapproved dependency build script. Retain the tarballs at their configured paths for future installs. Registry lookups for `@latch-local/*` usually mean an override is missing or points at the wrong directory. ## 3. Add your configuration and mount marker diff --git a/docs/quickstart.md b/docs/quickstart.md index 08f6359..52fd5a2 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -11,7 +11,7 @@ Install Git, Node 22 or later, and pnpm 10.17.1 or later. The repository pins pn Run these commands in a terminal: ```sh -git clone --branch v1.0.0 --depth 1 https://github.com/agammann/latch.git +git clone --branch v1.0.1 --depth 1 https://github.com/agammann/latch.git cd latch pnpm install --frozen-lockfile --ignore-scripts pnpm build diff --git a/examples/catalog/.latch/manifest.json b/examples/catalog/.latch/manifest.json index e27617c..17f8d17 100644 --- a/examples/catalog/.latch/manifest.json +++ b/examples/catalog/.latch/manifest.json @@ -1,10 +1,10 @@ { "version": 1, - "generatorVersion": "1.0.0", + "generatorVersion": "1.0.1", "files": { "src/latch.generated/integration.ts": { "before": null, - "after": "113580268f942f385c2285762dec1c41a07718d9d976f6d1eb934670f362c779" + "after": "0821f8af189918d1fdc0fde49b5c8189bf65fc19555bb463d06024bb5f1242d9" }, "src/latch.generated/env.d.ts": { "before": null, diff --git a/examples/catalog/package.json b/examples/catalog/package.json index 86e08ff..a4ee981 100644 --- a/examples/catalog/package.json +++ b/examples/catalog/package.json @@ -1,7 +1,7 @@ { "name": "latch-example-catalog", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1 --port 5173 --strictPort", diff --git a/examples/catalog/src/latch.generated/integration.ts b/examples/catalog/src/latch.generated/integration.ts index 95c6691..ecc5ab5 100644 --- a/examples/catalog/src/latch.generated/integration.ts +++ b/examples/catalog/src/latch.generated/integration.ts @@ -1,4 +1,4 @@ -// Generated by Latch 1.0.0. Edit latch.config.json and application handlers, then regenerate. +// Generated by Latch 1.0.1. Edit latch.config.json and application handlers, then regenerate. import { useLatch, type ScopeObserver } from '@latch-local/react'; import type { Handler, Contract } from '@latch-local/contracts'; export type Input0 = { "query"?: string; "category"?: "all" | "workspace" | "outdoors"; "sort"?: "name" | "price-asc" | "price-desc" }; diff --git a/examples/docs/.latch/manifest.json b/examples/docs/.latch/manifest.json index 6621d61..146d9fc 100644 --- a/examples/docs/.latch/manifest.json +++ b/examples/docs/.latch/manifest.json @@ -1,10 +1,10 @@ { "version": 1, - "generatorVersion": "1.0.0", + "generatorVersion": "1.0.1", "files": { "src/latch.generated/integration.ts": { "before": null, - "after": "465b4160a555317818dcca8259c6734df98ed2b91ce046f9977aaf2136a3f213" + "after": "acfc48e253c7ba258198d0f12e01094916c9c951208ca4bc63b036ae8a08b0ae" }, "src/latch.generated/env.d.ts": { "before": null, diff --git a/examples/docs/package.json b/examples/docs/package.json index 0e7c708..f8e80ab 100644 --- a/examples/docs/package.json +++ b/examples/docs/package.json @@ -1,7 +1,7 @@ { "name": "latch-example-docs", "private": true, - "version": "1.0.0", + "version": "1.0.1", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1 --port 5174 --strictPort", diff --git a/examples/docs/src/latch.generated/integration.ts b/examples/docs/src/latch.generated/integration.ts index 9eef842..5742650 100644 --- a/examples/docs/src/latch.generated/integration.ts +++ b/examples/docs/src/latch.generated/integration.ts @@ -1,4 +1,4 @@ -// Generated by Latch 1.0.0. Edit latch.config.json and application handlers, then regenerate. +// Generated by Latch 1.0.1. Edit latch.config.json and application handlers, then regenerate. import { useLatch, type ScopeObserver } from '@latch-local/react'; import type { Handler, Contract } from '@latch-local/contracts'; import { searchDocs as handler0 } from "../library"; diff --git a/package.json b/package.json index 010a863..b6e9a2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "latch-release-workspace", - "version": "1.0.0", + "version": "1.0.1", "private": true, "type": "module", "packageManager": "pnpm@10.17.1", diff --git a/packages/browser/README.md b/packages/browser/README.md index ba800bc..fd1bb87 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -1,4 +1,4 @@ -# @latch-local/browser 1.0.0 +# @latch-local/browser 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/browser/package.json b/packages/browser/package.json index 526d00f..319406d 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/browser", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch browser: Connect your site. Verify every action.", "license": "MIT", diff --git a/packages/cli/README.md b/packages/cli/README.md index 61b244d..1ec3e76 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,4 +1,4 @@ -# @latch-local/cli 1.0.0 +# @latch-local/cli 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/cli/package.json b/packages/cli/package.json index 91a214b..3b0ae37 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/cli", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch cli: Connect your site. Verify every action.", "license": "MIT", diff --git a/packages/cli/src/version.ts b/packages/cli/src/version.ts index 059488a..e96d287 100644 --- a/packages/cli/src/version.ts +++ b/packages/cli/src/version.ts @@ -1 +1 @@ -export const VERSION = '1.0.0'; +export const VERSION = '1.0.1'; diff --git a/packages/console/package.json b/packages/console/package.json index 92f3ca7..e40f88d 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/console-source", - "version": "1.0.0", + "version": "1.0.1", "private": true, "type": "module", "dependencies": { diff --git a/packages/contracts/README.md b/packages/contracts/README.md index f994185..6f86cdd 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -1,4 +1,4 @@ -# @latch-local/contracts 1.0.0 +# @latch-local/contracts 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/contracts/package.json b/packages/contracts/package.json index dcf0da0..6cfe9f4 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/contracts", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch contracts: Connect your site. Verify every action.", "license": "MIT", diff --git a/packages/react/README.md b/packages/react/README.md index c624a10..cd2d1f4 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -1,4 +1,4 @@ -# @latch-local/react 1.0.0 +# @latch-local/react 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/react/package.json b/packages/react/package.json index ece2a26..8ead9a3 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/react", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch react: Connect your site. Verify every action.", "license": "MIT", diff --git a/packages/runtime/README.md b/packages/runtime/README.md index e83341a..f90606e 100644 --- a/packages/runtime/README.md +++ b/packages/runtime/README.md @@ -1,4 +1,4 @@ -# @latch-local/runtime 1.0.0 +# @latch-local/runtime 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 062eea5..28d3df8 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/runtime", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch runtime: Connect your site. Verify every action.", "license": "MIT", diff --git a/packages/test/README.md b/packages/test/README.md index babda97..a5e5aae 100644 --- a/packages/test/README.md +++ b/packages/test/README.md @@ -1,4 +1,4 @@ -# @latch-local/test 1.0.0 +# @latch-local/test 1.0.1 Part of Latch: Connect your site. Verify every action. diff --git a/packages/test/package.json b/packages/test/package.json index 0db726f..e7d5215 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -1,6 +1,6 @@ { "name": "@latch-local/test", - "version": "1.0.0", + "version": "1.0.1", "type": "module", "description": "Latch test: Connect your site. Verify every action.", "license": "MIT", diff --git a/scripts/release.cjs b/scripts/release.cjs index 76e10aa..3d15ca3 100644 --- a/scripts/release.cjs +++ b/scripts/release.cjs @@ -3,9 +3,9 @@ const path = require('node:path'); const crypto = require('node:crypto'); module.exports = async ({ github, context, core }) => { - if (context.eventName !== 'push' || context.ref !== 'refs/heads/main' || + if (context.eventName !== 'workflow_dispatch' || context.ref !== 'refs/heads/main' || !/^[0-9a-f]{40}$/.test(context.sha)) { - throw new Error('Publishing requires a main push with an exact commit SHA.'); + throw new Error('Publishing requires a manual run on main with an exact commit SHA.'); } const version = JSON.parse(fs.readFileSync('package.json', 'utf8')).version; if (!/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/.test(version)) { @@ -66,7 +66,9 @@ module.exports = async ({ github, context, core }) => { } return commit; }; - await requireCorrectTag(); + if (await requireCorrectTag() !== context.sha) { + throw new Error('Create and verify the exact version tag before dispatching publication.'); + } const directory = 'release-artifacts'; const filenames = fs.readdirSync(directory).sort(); @@ -164,11 +166,7 @@ module.exports = async ({ github, context, core }) => { if (!latest.draft || latest.target_commitish !== context.sha || latest.tag_name !== tag) { throw new Error('Draft changed during upload; publication stopped.'); } - // Create the tag at the checked commit explicitly; target_commitish cannot move an existing tag. await requireCurrentHead(); - if (await requireCorrectTag() === null) { - await github.rest.git.createRef({ owner, repo, ref: 'refs/tags/' + tag, sha: context.sha }); - } if (await requireCorrectTag() !== context.sha) { throw new Error('Release tag is missing before publication.'); } diff --git a/tests/generator.test.mjs b/tests/generator.test.mjs index 114cbf6..0882f60 100644 --- a/tests/generator.test.mjs +++ b/tests/generator.test.mjs @@ -66,14 +66,14 @@ test('a valid 0.1.0 ownership manifest upgrades and keeps rollback protection', generate(r); apply(r); const output = path.join(r, 'src/latch.generated/integration.ts'); - fs.writeFileSync(output, fs.readFileSync(output, 'utf8').replace('Latch 1.0.0.', 'Latch 0.1.0.')); + fs.writeFileSync(output, fs.readFileSync(output, 'utf8').replace('Latch 1.0.1.', 'Latch 0.1.0.')); const manifestPath = path.join(r, '.latch/manifest.json'); const old = JSON.parse(fs.readFileSync(manifestPath)); old.generatorVersion = '0.1.0'; old.files['src/latch.generated/integration.ts'].after = createHash('sha256').update(fs.readFileSync(output)).digest('hex'); fs.writeFileSync(manifestPath, JSON.stringify(old)); generate(r); - assert.equal(JSON.parse(fs.readFileSync(manifestPath)).generatorVersion, '1.0.0'); + assert.equal(JSON.parse(fs.readFileSync(manifestPath)).generatorVersion, '1.0.1'); assert.equal(check(r).installation, 'applied'); rollback(r); assert.equal(fs.readFileSync(path.join(r, 'src/main.tsx'), 'utf8'), before);