Skip to content
Merged
18 changes: 9 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ jobs:
matrix:
node-version: [22, 24]
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
Expand All @@ -31,7 +31,7 @@ jobs:
env:
RELEASE_TARBALL_OUT: ${{ runner.temp }}/release-assets/geoptimize-${{ matrix.node-version }}.tgz
RELEASE_MANIFEST_OUT: ${{ runner.temp }}/release-candidate-node-${{ matrix.node-version }}.json
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-candidate-node-${{ matrix.node-version }}
path: ${{ runner.temp }}/release-candidate-node-${{ matrix.node-version }}.json
Expand All @@ -46,7 +46,7 @@ jobs:
node scripts/prepare-release-artifacts.mjs "$RUNNER_TEMP/release-assets"
(cd "$RUNNER_TEMP/release-assets" && sha256sum --check SHA256SUMS)
npm pack "$RUNNER_TEMP/release-assets/geoptimize-$version.tgz" --dry-run --ignore-scripts --json > "$RUNNER_TEMP/release-assets/package-preview.json"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: matrix.node-version == 24
with:
name: release-assets
Expand All @@ -57,7 +57,7 @@ jobs:
needs: test-and-build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: release-candidate-node-*
path: release-candidates
Expand All @@ -81,10 +81,10 @@ jobs:
lint-readme-commands:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- run: npm ci
Expand All @@ -97,10 +97,10 @@ jobs:
action-contract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- run: npm ci
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ jobs:
matrix:
language: [javascript-typescript, actions]
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: github/codeql-action/init@6f5948dfacef28e207b48d0905cf90c03365536d # v3 (2026-09-09)
- uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13)
with:
languages: ${{ matrix.language }}
build-mode: none
- uses: github/codeql-action/analyze@6f5948dfacef28e207b48d0905cf90c03365536d # v3 (2026-09-09)
- uses: github/codeql-action/analyze@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13)
with:
category: /language:${{ matrix.language }}
2 changes: 1 addition & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,21 @@ jobs:
RELEASE_TAG: ${{ inputs.tag }}
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.sha }}
fetch-depth: 0
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: release-assets
path: ${{ runner.temp }}/release-assets
- name: Verify source, signed tag, candidate and unpublished version
run: bash scripts/verify-release-publication.sh "$RUNNER_TEMP/release-assets"
- uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3
- uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: ${{ runner.temp }}/release-assets/geoptimize-*.tgz
- name: Stage all assets in a draft release
Expand All @@ -77,4 +77,4 @@ jobs:
- name: Verify public npm, GitHub and tag alignment
run: |
sha=$(jq -r .sha256 "$RUNNER_TEMP/release-assets/candidate.json")
bash scripts/verify-release-v0.8.sh "$GITHUB_SHA" "$sha"
bash scripts/verify-release-public.sh "$GITHUB_SHA" "$sha"
8 changes: 4 additions & 4 deletions .github/workflows/scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,20 @@ jobs:
security-events: write
id-token: write
steps:
- uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
- uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
with:
results_file: results.sarif
results_format: sarif
publish_results: true
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: scorecard-results
path: results.sarif
if-no-files-found: error
retention-days: 14
- uses: github/codeql-action/upload-sarif@6f5948dfacef28e207b48d0905cf90c03365536d # v3 (2026-09-09)
- uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 (2026-09-13)
with:
sarif_file: results.sarif
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,30 @@ All notable user-visible changes will be documented here. The project follows Se

## Unreleased

### Added

- Added a terminal menu for bare CLI invocations that scans a selected URL, file, or directory and saves a new offline HTML report.
- Preserved explicit commands, JSON output, and non-interactive invocation behavior.

### Changed

- Updated Puppeteer Core, Chalk, Commander and Vitest within the existing Node 22.12/24 support range; retained Node 24 type definitions.
- Updated verified GitHub Action pins and aligned the compatibility Action and sample workflow.

### Fixed

- Added bounded npm visibility polling and verification against the archived release commit and lockfile, without repeating publication.

### Security

- Clarified how bundled skills handle untrusted page content, repositories, and scan reports within the approved task scope.

### Documentation

- Updated current-state documentation after the verified v0.10.0 publication.
- Aligned migration, maintainer-security, OpenSSF, Action sample, and plugin descriptions with the published release.
- Completed both Claude Code installation steps in the English and translated guides and recorded directory submission results.
- Replaced the unsupported hosted-platform price estimate with vendor-specific pricing wording.

## 0.10.0

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ npx geoptimize audit https://example.com --json
npx geoptimize audit-site https://example.com --max-pages 20 --json
```

The next release adds a terminal menu for scanning a target and saving an HTML report. See [the interactive CLI guide](docs/interactive-cli.md) for development-checkout usage; published `0.10.0` uses the explicit commands above.

For measured Google Search experiments, initialize the separate SEO ledger:

```bash
Expand Down Expand Up @@ -183,7 +185,7 @@ also accepts an empty output directory for inspecting readiness and site JSON re
| Deterministic | Yes — versioned rules, fixture-tested | Partially | No — model output varies run to run |
| Runs where | Local CLI, CI, pre-commit hook, Vite/Next plugins | Browser / DevTools | Vendor cloud |
| Blocks regressions in CI | Yes, via a stable `--json` contract | Possible with extra wiring | Rarely |
| Cost | Free, MIT | Free | Typically $95+/mo |
| Cost | Free, MIT | Free | Varies by vendor |

Visibility trackers answer "did rankings change?". geoptimize answers the question you can act on in a pull request: "is this page ready?". The two compose rather than compete.

Expand Down Expand Up @@ -315,6 +317,7 @@ The hook checks staged `.html`, `.htm`, `.md`, and `.mdx` content. Review the ba

```bash
claude plugin marketplace add cucuwang/geoptimize
claude plugin install geoptimize@geoptimize
```

Or install the same reusable skills through the cross-agent Agent Skills CLI (skills.sh indexes installs from this command; there is no separate submit form):
Expand Down
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ runs:
using: composite
steps:
- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'

Expand Down
2 changes: 1 addition & 1 deletion action/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ runs:
using: composite
steps:
- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'

Expand Down
16 changes: 8 additions & 8 deletions docs/action-pins.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Verified upstream Action pins

Resolved through official GitHub repositories on 2026-09-09. Annotated tag objects
Resolved through official GitHub repositories on 2026-09-13. Annotated tag objects
were dereferenced to commits; tag-object SHAs are not used as Action pins.

| Action | Upstream tag | Full commit |
| --- | --- | --- |
| actions/checkout | v4.3.0 | 08eba0b27e820071cde6df949e0beb9ba4906955 |
| actions/setup-node | v4.4.0 | 49933ea5288caeca8642d1e84afbd3f7d6820020 |
| actions/upload-artifact | v4.6.2 | ea165f8d65b6e75b540449e92b4886f43607fa02 |
| actions/download-artifact | v4.3.0 | d3f86a106a0bac45b974a628896c90dbdf5c8093 |
| actions/checkout | v7.0.1 | 3d3c42e5aac5ba805825da76410c181273ba90b1 |
| actions/setup-node | v7.0.0 | 820762786026740c76f36085b0efc47a31fe5020 |
| actions/upload-artifact | v7.0.1 | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a |
| actions/download-artifact | v8.0.1 | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c |
| actions/dependency-review-action | v5.0.0 | a1d282b36b6f3519aa1f3fc636f609c47dddb294 |
| github/codeql-action | v3, resolved on assessment date | 6f5948dfacef28e207b48d0905cf90c03365536d |
| ossf/scorecard-action | v2.4.3 | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a |
| actions/attest-build-provenance | v3, resolved on assessment date | 977bb373ede98d70efdf65b84cb5f73e068dcc2a |
| github/codeql-action | v4.37.9 | cdf488f595d80d6e07e03d4674febd5ab45fa938 |
| ossf/scorecard-action | v2.4.4 | 2d1146689b8cda280b9bc96326124645441f03bc |
| actions/attest-build-provenance | v4.2.2 | 4d101475d8b20a2381f78447822ac1eab6504dd8 |

Recheck with `GET /repos/{owner}/{repo}/git/ref/tags/{tag}`. When object.type is
`tag`, follow object.url until object.type is `commit`. Cross-check the official
Expand Down
25 changes: 22 additions & 3 deletions docs/action-reproducibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,20 @@

The v0.10 trust-hardening review retained the composite Action and left bundle
evaluation to [issue #20](https://github.com/cucuwang/geoptimize/issues/20).
Both Action metadata paths now pin setup-node to an upstream commit. The default
package-spec remains the exact published geoptimize version. No runtime behavior
or package-spec override has been removed.
Both Action metadata paths now pin setup-node v7.0.0 to its verified upstream
commit. setup-node v7 runs on the Node 24 Action runtime; self-hosted runners
must be at least `2.327.1`. The default package-spec remains the exact published
geoptimize version. The scan's runtime behavior and package-spec override remain
unchanged.

The Node 24 runtime requirement applies to the Action runner that loads
setup-node. It is separate from the `node-version` input used to select the
project's Node 22 or Node 24 toolchain.

The copyable sample workflow now uses the same verified checkout commit and
sets `persist-credentials: false`. This is a sample-only consistency follow-up
for the mutable `actions/checkout@v4` line already present at the fixed base; it
was not introduced by PR #28.

| Dimension | A: runtime npm install | B: checked-in bundled JS |
| --- | --- | --- |
Expand All @@ -21,6 +32,14 @@ preserves much of the current surface. Puppeteer-core/browser paths, ESM depende
and dynamic imports need bundle testing even though the Action's normal scan is local.
A broad runtime rewrite remains outside this decision.

## Verification boundary

Local verification covers the pinned references, YAML parsing, and the existing
Action-contract checks in this checkout. Those checks do not prove that a hosted
runner executed the updated Actions. Before claiming hosted compatibility, read back
a Node 24 hosted run covering the Action contract and release artifacts, including
the self-hosted runner requirement when that environment is used.

Follow-up acceptance: prototype size and cold-run timing; build the bundle twice from
npm ci and compare hashes; preserve both metadata paths, package-spec semantics,
advisory/blocking behavior and outputs; inspect included licenses and optional browser
Expand Down
78 changes: 78 additions & 0 deletions docs/dependency-candidate-validation-2026-09-13.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Dependency Candidate Validation 2026-09-13

## Candidate and scope

The candidate is an isolated clone at
`/private/tmp/geoptimize-dependency-candidate-20260913`, created from the follow-through
worktree and fixed at `652b0344ba64409490151f148eed81606f60133c`. The source worktree,
its package files, and its `node_modules` were not modified. No commit, push, merge,
release, or external publication was performed.

This candidate combines the exact target versions from #23, #25, #26, and #27 while
leaving the package version at `0.10.0` and preserving all other package metadata:

| Package | package.json spec | lockfile resolution |
| --- | --- | --- |
| `puppeteer-core` | `^25.10.0` | 25.10.0 |
| `chalk` | `^6.0.0` | 6.0.0 |
| `commander` | `^15.0.0` | 15.0.0 |
| `vitest` | `^5.0.0` | 5.0.0 |
| `@types/node` | `^24.0.0` | 24.13.3, unchanged baseline |
| `undici-types` | transitive | 7.18.2, unchanged baseline |

The direct package specs were edited in the candidate, then the lock was regenerated
with npm 11.19.0 using `--package-lock-only --ignore-scripts` and the dedicated cache
`/private/tmp/geoptimize-follow-through-npm-cache`. The regenerated lock resolves the
four requested targets and preserves integrity values. Current registry ranges also
refreshed 45 lock package paths, including Vitest/Vite, Rolldown, and related transitive
entries. This is disclosed scope from lock regeneration; no source or test was changed.

Candidate file SHA-256 values:

| File | SHA-256 |
| --- | --- |
| `package.json` | `5213c547637ad0f093225d0c4191745c29b762827a3b5103ad6a7baffac05f06` |
| `package-lock.json` | `d6f68d41756d6e1b74dd0772f02992a1937f3179316f60a45dc3bfa5120c35b1` |

`npm ls --depth=0 --omit=optional` read back the four target versions, baseline
`@types/node@24.13.3`, and package `geoptimize@0.10.0`. Both CLI runtime checks returned
`0.10.0`.

## Verification results

| Runtime | npm | `npm ci` | `npm run check` | Action contract | Audit | Pack preview |
| --- | --- | --- | --- | --- | --- | --- |
| Node 24.11.1 | 11.19.0 | PASS, 103 packages | PASS, 19 files and 263 tests plus `tsc` | PASS | PASS, 0 vulnerabilities | PASS |
| Node 22.12.0 | 10.9.0 | PASS, 103 packages | PASS, 19 files and 263 tests plus `tsc` | PASS | PASS, 0 vulnerabilities | PASS |

The Node 22.12.0 Darwin arm64 runtime came from the official Node distribution. Its
tarball SHA-256 `293dcc6c2408da21562d135b0412525e381bb6fe150d688edb58fe850d0f3e13`
matched the official `SHASUMS256.txt`. The Node 24.11.1 runtime was already available
locally and was read back directly.

Both pack previews produced the same metadata:

- `geoptimize-0.10.0.tgz`
- packed size 1,169,636 bytes
- unpacked size 1,829,039 bytes
- preview shasum `634c2d4f68a3d0f3b84028d2130f92e439799518`
- preview integrity `sha512-fK7Zb6J2o50wuzP4kMVXeXdE9Ns/Kf6M1dCG/8w7qA8PXvCS/LG4BNACoI9AC1ICul55iSO9m9bmNrzS0hexOw==`

The preview was run with `--dry-run`; no release tarball was created. The main agent subsequently verified Puppeteer 25.10.0 against Chrome 153.0.8010.36 on Node 24.11.1 and Node 22.12.0 using an isolated temporary browser profile and an offline JavaScript fixture. Both returned `rendered:5`. This exercises browser launch, navigation, DOM execution and close; it does not prove behavior against arbitrary public SPA sites. The script is `/private/tmp/geoptimize-pr-spec-evidence-20260913/browser-smoke.mjs`.

## Commands

```text
cd /private/tmp/geoptimize-dependency-candidate-20260913
npm install --package-lock-only --ignore-scripts --no-audit --no-fund --cache /private/tmp/geoptimize-follow-through-npm-cache
npm ci --ignore-scripts --no-audit --no-fund --cache /private/tmp/geoptimize-follow-through-npm-cache
npm run check
bash action/test-contract.sh
npm audit --audit-level=high --cache /private/tmp/geoptimize-follow-through-npm-cache
npm pack --dry-run --ignore-scripts --cache /private/tmp/geoptimize-follow-through-npm-cache --json
PATH=/private/tmp/geoptimize-node22.12.0/node-v22.12.0-darwin-arm64/bin:$PATH npm ci --ignore-scripts --no-audit --no-fund --cache /private/tmp/geoptimize-follow-through-npm-cache
PATH=/private/tmp/geoptimize-node22.12.0/node-v22.12.0-darwin-arm64/bin:$PATH npm run check
PATH=/private/tmp/geoptimize-node22.12.0/node-v22.12.0-darwin-arm64/bin:$PATH bash action/test-contract.sh
PATH=/private/tmp/geoptimize-node22.12.0/node-v22.12.0-darwin-arm64/bin:$PATH npm audit --audit-level=high --cache /private/tmp/geoptimize-follow-through-npm-cache
PATH=/private/tmp/geoptimize-node22.12.0/node-v22.12.0-darwin-arm64/bin:$PATH npm pack --dry-run --ignore-scripts --cache /private/tmp/geoptimize-follow-through-npm-cache --json
```
Loading
Loading