Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 104 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,92 @@ on:
tags:
- "v*"

concurrency: ${{ github.workflow }}-${{ github.ref }}

permissions:
contents: write
id-token: write

jobs:
verify:
name: Verify (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- name: Check repository hygiene
run: |
forbidden_files="$(git ls-files | grep -E '(^|/)(AGENTS\.md|CLAUDE\.md|\.DS_Store|\.dev-session|\.staff-engineer-state\.json|\.staff-engineer\.json)$|(^|/)(docs/plans|\.omx|\.bap|\.banners)(/|$)' || true)"

if [ -n "$forbidden_files" ]; then
echo "Tracked internal-only files found in public release tree."
printf '%s\n' "$forbidden_files"
exit 1
fi

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org
cache: npm

- run: npm ci
- name: Check release policy
run: node scripts/check-release-policy.mjs
- name: Check tag and package version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_VERSION="$(node -p "require('./package.json').version")"
test "$TAG_VERSION" = "$PKG_VERSION"
VERSION="$PKG_VERSION" node - <<'NODE'
const version = process.env.VERSION;
const stableSemverPattern = /^\d+\.\d+\.\d+$/;
const rcSemverPattern = /^\d+\.\d+\.\d+-rc\.\d+$/;

if (!stableSemverPattern.test(version) && !rcSemverPattern.test(version)) {
console.error(`Release tags must match x.y.z or x.y.z-rc.N, got ${version}`);
process.exit(1);
}
NODE
- name: Build package
run: npm run build
- name: Typecheck
run: npm run typecheck
- name: Run Vitest suite
run: npm test
- name: Run performance budget tests
run: npm run test:perf
- name: Check npm audit clean state
run: npm audit --json
- name: Check package contents
run: |
pack_json="$(npm pack --dry-run --json)"
PACK_JSON="$pack_json" node - <<'NODE'
const [{ files }] = JSON.parse(process.env.PACK_JSON);
const forbidden = files
.map((file) => file.path)
.filter((path) =>
/(^|\/)(AGENTS\.md|CLAUDE\.md|AUDIT\.md|RELEASE_PLAN\.md|\.DS_Store|\.dev-session|\.staff-engineer-state\.json|\.staff-engineer\.json)$/.test(path) ||
/(^|\/)(docs\/plans|\.omx|\.bap|\.banners|coverage)(\/|$)/.test(path)
);

if (forbidden.length > 0) {
console.error("Internal-only files would be included in npm package:");
for (const path of forbidden) {
console.error(`- ${path}`);
}
process.exit(1);
}

console.log(`Package content check passed (${files.length} files).`);
NODE

publish:
runs-on: ubuntu-latest
needs: verify
steps:
- uses: actions/checkout@v4

Expand All @@ -22,13 +102,34 @@ jobs:

- run: npm ci
- run: npm run build
- run: npm run typecheck
- run: npm test
- run: npm publish
- name: Resolve release policy
id: release_policy
run: |
VERSION="$(node -p "require('./package.json').version")"
VERSION="$VERSION" node - <<'NODE' >> "$GITHUB_OUTPUT"
const version = process.env.VERSION;
const stableSemverPattern = /^\d+\.\d+\.\d+$/;
const rcSemverPattern = /^\d+\.\d+\.\d+-rc\.\d+$/;

if (rcSemverPattern.test(version)) {
console.log("npm_tag=rc");
console.log("github_prerelease=true");
} else if (stableSemverPattern.test(version)) {
console.log("npm_tag=latest");
console.log("github_prerelease=false");
} else {
console.error(`Release tags must match x.y.z or x.y.z-rc.N, got ${version}`);
process.exit(1);
}
NODE
- name: Re-check release policy before publish
run: node scripts/check-release-policy.mjs
- run: npm publish --access public --provenance --tag "${{ steps.release_policy.outputs.npm_tag }}"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
prerelease: ${{ steps.release_policy.outputs.github_prerelease }}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
node_modules
dist
coverage
*.tgz
.turbo
.omx
.bap
.banners
.DS_Store
.dev-session
.staff-engineer-state.json
.staff-engineer.json
AUDIT.md
AGENTS.md
docs/plans/
CLAUDE.md
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Changelog

## [1.0.0-rc.1] - 2026-05-04

`@pyyush/useid@1.0.0-rc.1` is published on npm under the `rc` dist-tag. The npm `latest` dist-tag remains `0.1.0` as of May 5, 2026, so install this release candidate with `npm install @pyyush/useid@rc`.

### Runtime support

- The supported and tested runtime matrix for this RC is Node.js 20 and 22.
- The package `engines.node` claim is intentionally narrowed to `^20.0.0 || ^22.0.0` until extraction budget evidence covers newer Node versions.
- CI and release verification run the same Node 20/22 matrix.

### Migration notes from published 0.1.0

- Treat `ResolveResult` as the stable discriminated union: branch on `result.resolved` before reading success or failure fields.
- Handle every stable abstention reason: `binding_mismatch`, `no_candidates`, `below_threshold`, and `ambiguous_match`.
- Keep custom scoring weights normalized. `semantic + structural + spatial` must sum to `1`.
- Expect stricter safe-abstention behavior for duplicate names, missing accessible-name evidence, role drift, incomplete layout evidence, and DOM/accessibility mismatches.
- Do not use redacted signatures for later resolution. Redacted output is for logs and support bundles only.
- Browser-harness alignment is docs/examples/design alignment only. There is no browser-harness dependency, backend, runtime bridge, or required browser runtime.

### Security and observability guidance

- Raw snapshots, signatures, candidate diagnostics, and explanations can contain page text, labels, accessible names, accessible descriptions, sibling tokens, and form labels.
- Prefer `redactUSEID()` before logging or storing signatures outside short-lived debug paths.
- Log stable operational fields such as `resolved`, `abstentionReason`, `confidence`, `scores`, `scoreGap`, candidate count, threshold, margin, and signature hash.
- Avoid logging raw unresolved candidate names by default; use raw explanations only in controlled debug/support contexts.
- Use redacted production log shapes for browser-harness grounding events. Keep raw `explainResolution()` output limited to local debugging or controlled support bundles.

### Browser-harness alignment

- Added docs for the snapshot boundary a browser harness must provide: current URL, DOM snapshot, accessibility snapshot, and optional frame path.
- Added a grounding-gate example that resolves before click/fill-style actions and records confidence, score gap, abstention reason, and score bands without raw candidate names.
- Clarified that README examples target the `1.0.0-rc.1` API, not the npm `latest` package while `latest` remains `0.1.0`.
- Documented the current fixed `1024x768` spatial normalization limit so browser-harness adopters do not mistake it for a browser viewport matrix.
- Confirmed the release scope is `learn-from` only: no browser-harness dependency, backend, runtime bridge, or required browser runtime.

## [0.2.0] - 2026-04-02

Pre-RC local release baseline used during 1.0 planning. This version was not the npm `latest` at the time of the planning work and is superseded by `1.0.0-rc.1`.

### Added

- clearer grounding-gate documentation and repo-local example
- capture fingerprint support and richer resolution explainability
- stronger extractor, matcher, resolver, and safety coverage
- release verification and npm package metadata improvements

### Changed

- kept the product narrowly focused on safe grounding and abstention
- improved candidate generation and structural scoring for repeated or shifted elements
- clarified current support matrix and honest abstention cases
Loading
Loading