From 76568043499ab4aa29257093211939322c8cf312 Mon Sep 17 00:00:00 2001 From: Layne Penney Date: Wed, 15 Jul 2026 04:50:45 -0500 Subject: [PATCH] fix(extract): npm 0.6.0 version-sync + workflow_dispatch + already-published guard Opus's decision (no workflow_dispatch on publish-npm.yml today, so the automatic release- triggered publish for extract#31/79acca7's release already fired-and-failed against the stale 0.5.0 npm version -- there is no automatic retry path once npm's version catches up). 1. packages/ts/package.json + package-lock.json: 0.5.0 -> 0.6.0, via `npm version --no-git-tag-version` (keeps both files in sync through npm's own tooling, not hand-edited sed -- avoiding the exact lockfile-drift class Sentinel caught earlier this session). Semantically honest: main already carries the full additive-role ts/py parity slice (schema.ts/builder.ts/validate.ts/finalize.ts/prompt-data.ts all match Python), so the version number now matches what's actually shipped, not a hollow bump. 2. publish-npm.yml: added `workflow_dispatch` trigger alongside the existing `release: published` trigger, so this specific already-fired release can be manually re-triggered once the version genuinely changed. Guarded the release-only step (SBOM upload, which reads github.event.release.tag_name) behind `github.event_name == 'release'` so a manual dispatch doesn't crash on a nonexistent release context. 3. Added an "already published" guard: checks `npm view @synapt-dev/extract@$VERSION version` before publishing: skips publish/SBOM/upload-to-release when that version is already live. Fixes a real noise problem this exact release train just produced: the workflow fires unconditionally on EVERY GitHub release regardless of whether npm's OWN version actually changed, so a future Python-only release (npm version unchanged) will otherwise always fail-red with "cannot publish over previously published version" -- expected, not a bug, but noisy. The guard makes that case a clean skip instead of a red X. CHANGELOG.md: corrected the now-stale "npm keeps 0.5.0 for now" and "+ version-sync" claims from the earlier landed commit -- version-sync is done here; the FULL ts/py parity effort (a TS extract_batch/batch.ts port) remains the separate, still-deferred follow-up. Verified: 265 TS tests green, tsc --noEmit clean, no test hardcodes the package version (checked before assuming safe), publish-npm.yml YAML validated. Premium boundary: OSS -- extract's IL schema/build/CI infrastructure only. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017ZMaT1FQJD6rqfN77piMHm --- .github/workflows/publish-npm.yml | 20 ++++++++++++++++++++ CHANGELOG.md | 4 ++-- packages/ts/package-lock.json | 4 ++-- packages/ts/package.json | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index fe6ebc0..5aa1d25 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -3,6 +3,11 @@ name: Publish @synapt-dev/extract to npm on: release: types: [published] + workflow_dispatch: + # Manual re-trigger — needed because this workflow has no version-mismatch retry path: + # a `release` event fires it exactly once, and if npm's version hadn't moved yet at that + # point (e.g. a Python-only release, or this repo's own version-sync-deferred slices), + # there is no automatic second attempt once the npm version is later bumped. permissions: contents: write @@ -26,12 +31,27 @@ jobs: - run: npm run build + - name: Check if this version is already published + id: check + run: | + VERSION=$(node -p "require('./package.json').version") + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + if npm view "@synapt-dev/extract@$VERSION" version >/dev/null 2>&1; then + echo "already_published=true" >> "$GITHUB_OUTPUT" + echo "@synapt-dev/extract@$VERSION is already published to npm -- skipping publish (this is expected on every release where only the PyPI side bumped, not an error)." + else + echo "already_published=false" >> "$GITHUB_OUTPUT" + fi + - run: npm publish --provenance --access public + if: steps.check.outputs.already_published != 'true' - name: Generate SBOM + if: steps.check.outputs.already_published != 'true' run: npm sbom --omit=dev --sbom-format cyclonedx > sbom.cdx.json - name: Upload SBOM to release + if: steps.check.outputs.already_published != 'true' && github.event_name == 'release' env: GH_TOKEN: ${{ github.token }} run: gh release upload ${{ github.event.release.tag_name }} sbom.cdx.json --clobber diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d14ea2..514971a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.6.0 -Temporal validity role + resolution anchor — additive Stage-1 IL enrichment (config/design/extract-temporal-role-2026-07-14.md). `synapt-extract` (PyPI) bumps to 0.6.0. `@synapt-dev/extract` (npm) carries the same additive-role parity but keeps its version at 0.5.0 for now — the npm version-sync is part of the deferred full-parity effort, not this additive-role slice. +Temporal validity role + resolution anchor — additive Stage-1 IL enrichment (config/design/extract-temporal-role-2026-07-14.md). `synapt-extract` (PyPI) bumps to 0.6.0. `@synapt-dev/extract` (npm) also bumps to 0.6.0 — main already carries the full additive-role parity slice, so the version number is honest — but publishes on a short lag behind PyPI (npm's publish workflow had no `workflow_dispatch` retrigger, added alongside this bump). The FULL ts/py parity effort (a TS `extract_batch`/`batch.ts` port) remains the deferred post-validation follow-up, separate from this version-sync. - Added `role` (`effective` | `expiry` | `range` | `superseded` | `point`) to the temporal-ref schema, capturing the validity DIRECTION a date constrains (e.g. "expires April 30" → `expiry`, vs "effective March 2026" → `effective`) — a semantic distinction the source sentence carries but prior extraction dropped - `role` and `resolved_end` are now BASE-tier on the `temporal_refs` capability (no longer gated behind the separate `temporal_classes` capability) — always available to any caller requesting `temporal_refs`; `type`/`context` remain `temporal_classes`-gated @@ -11,7 +11,7 @@ Temporal validity role + resolution anchor — additive Stage-1 IL enrichment (c - Fixed a prompt-rendering gap where an absent `date` param rendered the literal string "Resolve relative dates using: None." instead of omitting the instruction (wrapped in `{{#if date}}`; supported identically by both prompt renderers) - `_detect_capabilities`'s `temporal_classes` heuristic now keys on `type` presence only (`resolved_end` no longer implies the gated capability was exercised, since it moved to base tier) - Published JSON schema (`schemas/temporal-ref/v1.json`, both the repo-root canonical copy and the Python package copy) updated to match — schema-drift-check green -- **ts/py parity — the additive `role` coherence slice IS included** (`@synapt-dev/extract` TypeScript): `schema.ts` types `role`, `builder.ts` emits `role`/`resolved_end` base-tier, `validate.ts` accepts/enum-validates `role` (+ `role === "range"` → `resolved_end`), `finalize.ts` drops the `resolved_end` inference, and the embedded prompt fragment matches the shared file byte-for-byte. TS and Python produce identical schema/validation/finalize output on the same inputs (verified cross-language). The FULL parity effort (a TS `extract_batch`/`batch.ts` port + version-sync) remains the post-validation follow-up per config/design/extract-ts-py-parity-plan-2026-07-15.md — this release only carries the additive-role coherence needed to keep the shared prompt/schema surface consistent. +- **ts/py parity — the additive `role` coherence slice IS included** (`@synapt-dev/extract` TypeScript): `schema.ts` types `role`, `builder.ts` emits `role`/`resolved_end` base-tier, `validate.ts` accepts/enum-validates `role` (+ `role === "range"` → `resolved_end`), `finalize.ts` drops the `resolved_end` inference, and the embedded prompt fragment matches the shared file byte-for-byte. TS and Python produce identical schema/validation/finalize output on the same inputs (verified cross-language). The FULL parity effort (a TS `extract_batch`/`batch.ts` port) remains the post-validation follow-up per config/design/extract-ts-py-parity-plan-2026-07-15.md — the version number now matches PyPI (see above), but the additive-role coherence slice is still what's shipped, not the full `batch.ts` port. ## v0.5.0 diff --git a/packages/ts/package-lock.json b/packages/ts/package-lock.json index 4556753..0fde876 100644 --- a/packages/ts/package-lock.json +++ b/packages/ts/package-lock.json @@ -1,12 +1,12 @@ { "name": "@synapt-dev/extract", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@synapt-dev/extract", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "devDependencies": { "@types/node": "^25.6.0", diff --git a/packages/ts/package.json b/packages/ts/package.json index b1e95b4..474977f 100644 --- a/packages/ts/package.json +++ b/packages/ts/package.json @@ -1,6 +1,6 @@ { "name": "@synapt-dev/extract", - "version": "0.5.0", + "version": "0.6.0", "description": "SynaptExtraction IL v1 -- schema, validation, and finalization", "type": "module", "main": "dist/index.js",