aeoptimize 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  },
6
6
  "metadata": {
7
7
  "description": "Content-readiness lint with evidence-bounded AI discovery experiments",
8
- "version": "0.6.0"
8
+ "version": "0.6.1"
9
9
  },
10
10
  "plugins": [
11
11
  {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aeoptimize",
3
3
  "description": "Content-readiness lint and evidence-bounded discovery experiments",
4
- "version": "0.6.0",
4
+ "version": "0.6.1",
5
5
  "author": {
6
6
  "name": "Te-Shu Wang"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable user-visible changes will be documented here. The project follows Semantic Versioning after the v0.6 evidence baseline is released.
4
4
 
5
+ ## 0.6.1
6
+
7
+ ### Fixed
8
+
9
+ - Bound public CLI verification to the downloaded tarball that passed SHA-256 verification.
10
+ - Added an executable clean-worktree release candidate gate and cross-Node package reproducibility check.
11
+ - Exercised the public rule corpus across the HTML parser boundary and strengthened release verifier failure tests.
12
+
5
13
  ## 0.6.0
6
14
 
7
15
  ### Changed
package/README.md CHANGED
@@ -64,7 +64,7 @@ node -e "const r=require('./aeoptimize-report.json'); process.exit(r.overall.tot
64
64
  The v0.6 GitHub Action is advisory by default. It reports findings without blocking the workflow:
65
65
 
66
66
  ```yaml
67
- - uses: cucuwang/aeoptimize@v0.6.0
67
+ - uses: cucuwang/aeoptimize@v0.6.1
68
68
  with:
69
69
  path: dist
70
70
  ```
@@ -72,7 +72,7 @@ The v0.6 GitHub Action is advisory by default. It reports findings without block
72
72
  Projects can explicitly choose blocking mode after accepting a baseline:
73
73
 
74
74
  ```yaml
75
- - uses: cucuwang/aeoptimize@v0.6.0
75
+ - uses: cucuwang/aeoptimize@v0.6.1
76
76
  with:
77
77
  path: dist
78
78
  fail-on-low-score: 'true'
package/ROADMAP.md CHANGED
@@ -14,7 +14,7 @@ Release gates:
14
14
  - root GitHub Action metadata and an end-to-end sample repository;
15
15
  - stable JSON output, changelog, release notes, and rollback instructions.
16
16
 
17
- The release gates are enforced by `src/core/__tests__/release-contract.test.ts`, the versioned corpus in `fixtures/v0.6/`, the copyable sample in `examples/github-action-sample/`, and the release runbook in `docs/release-v0.6.md`.
17
+ The code-level gates are enforced by `npm run release:check`, `src/core/__tests__/release-contract.test.ts`, the versioned corpus in `fixtures/v0.6/`, and the local composite Action contract. CI compares real Node.js 22 and 24 package manifests and hashes. Repository rulesets and the release runbook remain separate maintainer controls.
18
18
 
19
19
  ## Product validation after v0.6
20
20
 
package/dist/cli/index.js CHANGED
@@ -13,7 +13,7 @@ const program = new Command();
13
13
  program
14
14
  .name('aeoptimize')
15
15
  .description('Deterministic content-readiness lint for websites and documentation')
16
- .version('0.6.0');
16
+ .version('0.6.1');
17
17
  // ── scan command ───────────────────────────────────────────────────
18
18
  program
19
19
  .command('scan <target>')
@@ -1,39 +1,33 @@
1
1
  # v0.6 release and rollback guide
2
2
 
3
- Version 0.6.0 establishes the evidence-bounded scoring, packaging, and GitHub Action contracts. It is not released until npm, the Git tag, and the GitHub Release are each created and read back independently.
3
+ Version 0.6.1 hardens the v0.6 evidence-bounded scoring, packaging, and GitHub Action contracts. It is not released until npm, the Git tag, and the GitHub Release are each created and read back independently.
4
4
 
5
5
  ## Release acceptance
6
6
 
7
- Before publication, verify all of the following from the intended release commit:
7
+ Before publication, run `npm ci` and `npm run release:check` from the intended release commit. The candidate gate verifies all of the following:
8
8
 
9
- 1. `npm ci`, `npm run check`, `npm audit --audit-level=high`, and `bash action/test-contract.sh` pass.
9
+ 1. `npm run check`, `npm audit --audit-level=high`, and `bash action/test-contract.sh` pass.
10
10
  2. The public v0.6 rule corpus covers the positive, negative, and false-positive boundary for every scored rule.
11
- 3. `npm pack` is reproducible, its SHA-256 is recorded, and a clean consumer can invoke `aeoptimize`, `aeo`, and `aeo-cli`.
11
+ 3. An actual `npm pack` candidate contains the required public files, its SHA-256 is recorded, and a clean consumer can invoke `aeoptimize`, `aeo`, and `aeo-cli` from that exact tarball.
12
12
  4. CI succeeds on Node.js 22 and 24 for the release commit.
13
13
  5. The JSON automation contract and Action sample tests pass.
14
14
  6. The npm account is verified immediately before publishing.
15
15
 
16
+ CI runs the same candidate gate on Node.js 22 and 24 and compares version, filename, SHA-256, file count, and unpacked size. `prepublishOnly` invokes the candidate gate again and refuses a dirty worktree by default.
17
+
16
18
  Publishing, tagging, creating a GitHub Release, changing npm dist-tags, and deprecating a version are separate external mutations and require separate maintainer authorization.
17
19
 
18
20
  ## Release notes
19
21
 
20
- ### Evidence-bounded readiness scoring
21
-
22
- - Reframe the score as deterministic content readiness rather than a prediction of ranking or AI citation.
23
- - Treat FAQ structure and `llms.txt` as optional, zero-point signals.
24
- - Remove exact-one-H1 and fixed meta-description-length assumptions.
25
- - Flag unsourced quantitative claims instead of rewarding more numbers.
26
- - Publish a versioned fixture corpus with positive, negative, and false-positive boundaries for every scored rule.
27
-
28
- ### Reproducible automation
22
+ ### Artifact-bound release verification
29
23
 
30
- - Support maintained Node.js 22 and 24 lines.
31
- - Keep `aeoptimize`, `aeo`, and `aeo-cli` in the packed npm manifest.
32
- - Add an advisory-by-default GitHub Action with explicit blocking mode and stable JSON outputs.
33
- - Add a copyable end-to-end Action sample and release-time contract checks.
34
- - Refresh dependencies and require zero high or critical audit findings at release time.
24
+ - Build the candidate from a clean worktree and install all three CLI aliases from the exact tarball whose SHA-256 is recorded.
25
+ - Require byte-identical candidate manifests from Node.js 22 and 24 before the release can proceed.
26
+ - Bind the public verifier's CLI smoke checks to the downloaded, hash-verified npm tarball.
27
+ - Fail closed when a CLI alias, tag target, repository identity, or GitHub Release state does not match.
28
+ - Exercise every v0.6 rule fixture through the real HTML parser boundary.
35
29
 
36
- No ranking, traffic, indexing, rich-result, AI Overview, or citation outcome is claimed by this release.
30
+ No scoring rule, rule weight, JSON field, Action input, or Action output changes in this patch. No ranking, traffic, indexing, rich-result, AI Overview, or citation outcome is claimed by this release.
37
31
 
38
32
  ## Publication readback
39
33
 
@@ -41,16 +35,16 @@ After an authorized npm publication:
41
35
 
42
36
  ```bash
43
37
  npm view aeoptimize version dist-tags --json
44
- npm view aeoptimize@0.6.0 version gitHead repository homepage bugs dist --json
45
- consumer_root=$(mktemp -d "${TMPDIR:-/tmp}/aeoptimize-v0.6-consumer.XXXXXX")
46
- npm install --prefix "$consumer_root" aeoptimize@0.6.0
38
+ npm view aeoptimize@0.6.1 version gitHead repository homepage bugs dist --json
39
+ consumer_root=$(mktemp -d "${TMPDIR:-/tmp}/aeoptimize-v0.6.1-consumer.XXXXXX")
40
+ npm install --prefix "$consumer_root" aeoptimize@0.6.1
47
41
  "$consumer_root/node_modules/.bin/aeoptimize" --version
48
42
  "$consumer_root/node_modules/.bin/aeo" --version
49
43
  "$consumer_root/node_modules/.bin/aeo-cli" --version
50
44
  rm -rf -- "$consumer_root"
51
45
  ```
52
46
 
53
- After separately authorized tag and GitHub Release creation, verify that `v0.6.0` points to the tested release commit and that the Release is published rather than draft or prerelease.
47
+ After separately authorized tag and GitHub Release creation, verify that `v0.6.1` points to the tested release commit and that the Release is published rather than draft or prerelease.
54
48
 
55
49
  The fail-closed public verifier checks npm `latest`, the exact version, public repository identity, the downloaded tarball SHA-256, all three installed CLI aliases, the tag target, and the published GitHub Release. The tarball hash is the required artifact-identity gate. If npm exposes `gitHead`, it must match the expected release commit; absence is reported as informational because npm's publish contract guarantees tarball integrity but does not guarantee that metadata field.
56
50
 
@@ -62,11 +56,11 @@ bash scripts/verify-release-v0.6.sh <verified-release-commit> <verified-package-
62
56
 
63
57
  An npm dist-tag rollback changes what `npm install aeoptimize` selects; it does not remove exact-version installs. Never silently move an existing Git tag to different code.
64
58
 
65
- If npm 0.6.0 is unsuitable before a corrective release is available, request separate authorization for each mutation, then:
59
+ If npm 0.6.1 is unsuitable before a corrective release is available, request separate authorization for each mutation, then:
66
60
 
67
61
  ```bash
68
- npm dist-tag add aeoptimize@0.5.3 latest
69
- npm deprecate aeoptimize@0.6.0 "Use 0.5.3 until the corrective release is available."
62
+ npm dist-tag add aeoptimize@0.6.0 latest
63
+ npm deprecate aeoptimize@0.6.1 "Use 0.6.0 until the corrective release is available."
70
64
  ```
71
65
 
72
- Mark the GitHub Release with the same warning. Preserve the `v0.6.0` tag as evidence of what was published, fix forward in 0.6.1, rerun the complete release acceptance suite, and only then move npm `latest` to the corrective version.
66
+ Mark the GitHub Release with the same warning. Preserve the `v0.6.1` tag as evidence of what was published, fix forward in 0.6.2, rerun the complete release acceptance suite, and only then move npm `latest` to the corrective version.
@@ -12,7 +12,7 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
14
  - uses: actions/checkout@v4
15
- - uses: cucuwang/aeoptimize@v0.6.0
15
+ - uses: cucuwang/aeoptimize@v0.6.1
16
16
  with:
17
17
  path: site
18
18
  fail-on-low-score: 'false'
@@ -6,4 +6,4 @@ This directory is a copyable end-to-end sample for the v0.6 Action contract.
6
6
  - `site/index.html` is a deterministic public input.
7
7
  - The Action is advisory by default. The sample does not block a pull request on an unreviewed score threshold.
8
8
 
9
- The workflow becomes reproducible only after both `aeoptimize@0.6.0` exists on npm and the immutable `v0.6.0` Git tag points to the matching release commit. Until both artifacts exist, use the local CLI or the release-candidate package during controlled verification.
9
+ The workflow becomes reproducible only after both `aeoptimize@0.6.1` exists on npm and the immutable `v0.6.1` Git tag points to the matching release commit. Until both artifacts exist, use the local CLI or the release-candidate package during controlled verification.
@@ -126,14 +126,26 @@ export const ruleFixtureCorpus: Record<string, RuleFixtureSet> = {
126
126
  negative: {
127
127
  purpose: 'A majority of dangling pronoun or transition openings triggers a review finding.',
128
128
  document: {
129
- paragraphs: ['This is important.', 'They require context.', 'However, it varies.', 'The release is versioned.', 'The report is public.'],
129
+ paragraphs: [
130
+ 'This is important for the release.',
131
+ 'They require additional context.',
132
+ 'However, it varies by project.',
133
+ 'The release is versioned for users.',
134
+ 'The report is publicly available.',
135
+ ],
130
136
  },
131
137
  expected: { score: 3, issues: 1, suggestions: 1 },
132
138
  },
133
139
  boundary: {
134
140
  purpose: 'Exactly twenty percent dangling openings is the non-penalized ratio boundary.',
135
141
  document: {
136
- paragraphs: ['This needs context.', 'The package is versioned.', 'The Action is advisory.', 'The report is stable.', 'The fixture is public.'],
142
+ paragraphs: [
143
+ 'This needs context for the reader.',
144
+ 'The package is explicitly versioned.',
145
+ 'The Action is advisory by default.',
146
+ 'The report remains stable for automation.',
147
+ 'The fixture is publicly reviewable.',
148
+ ],
137
149
  },
138
150
  expected: { score: 8, issues: 0, suggestions: 1 },
139
151
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aeoptimize",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Deterministic content-readiness lint for static websites and documentation",
5
5
  "type": "module",
6
6
  "main": "./dist/core/index.js",
@@ -20,6 +20,7 @@
20
20
  "agents/",
21
21
  "fixtures/",
22
22
  "examples/github-action-sample/",
23
+ "scripts/verify-release-candidate.sh",
23
24
  "scripts/verify-release-v0.6.sh",
24
25
  ".claude-plugin/",
25
26
  "docs/methodology.md",
@@ -34,10 +35,11 @@
34
35
  "scripts": {
35
36
  "build": "tsc",
36
37
  "check": "npm test && npm run build",
38
+ "release:check": "bash scripts/verify-release-candidate.sh",
37
39
  "dev": "tsc --watch",
38
40
  "test": "vitest run",
39
41
  "test:watch": "vitest",
40
- "prepublishOnly": "npm run check"
42
+ "prepublishOnly": "npm run release:check"
41
43
  },
42
44
  "keywords": [
43
45
  "aeo",
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ REPO_ROOT=$(cd "$(dirname "$0")/.." && pwd)
5
+ cd "$REPO_ROOT"
6
+
7
+ for command_name in git jq mktemp node npm; do
8
+ if ! command -v "$command_name" >/dev/null 2>&1; then
9
+ echo "missing required command: $command_name" >&2
10
+ exit 2
11
+ fi
12
+ done
13
+
14
+ if [ "${ALLOW_DIRTY_RELEASE_CHECK:-0}" != "1" ] && [ -n "$(git status --porcelain)" ]; then
15
+ echo "release candidate must be built from a clean worktree" >&2
16
+ exit 1
17
+ fi
18
+
19
+ VERIFY_BASE=${TMPDIR:-/tmp}
20
+ VERIFY_BASE=${VERIFY_BASE%/}
21
+ VERIFY_ROOT=$(mktemp -d "$VERIFY_BASE/aeoptimize-release-candidate.XXXXXX")
22
+ PACK_ROOT="$VERIFY_ROOT/pack"
23
+ CONSUMER_ROOT="$VERIFY_ROOT/consumer"
24
+ PACK_JSON="$VERIFY_ROOT/pack.json"
25
+
26
+ cleanup() {
27
+ case "$VERIFY_ROOT" in
28
+ "$VERIFY_BASE"/aeoptimize-release-candidate.*)
29
+ rm -rf -- "$VERIFY_ROOT"
30
+ ;;
31
+ *)
32
+ echo "Refusing to remove unexpected verification path: $VERIFY_ROOT" >&2
33
+ ;;
34
+ esac
35
+ }
36
+
37
+ trap cleanup EXIT
38
+ mkdir -p "$PACK_ROOT"
39
+
40
+ npm run check
41
+ bash action/test-contract.sh
42
+ npm --cache "$VERIFY_ROOT/npm-cache" audit --audit-level=high
43
+ npm_config_dry_run=false npm --cache "$VERIFY_ROOT/npm-cache" \
44
+ pack --json --pack-destination "$PACK_ROOT" > "$PACK_JSON"
45
+
46
+ PACKAGE_FILENAME=$(jq -er '.[0].filename' "$PACK_JSON")
47
+ PACKAGE_VERSION=$(jq -er '.[0].version' "$PACK_JSON")
48
+ PACKAGE_FILE_COUNT=$(jq -er '.[0].files | length' "$PACK_JSON")
49
+ PACKAGE_UNPACKED_SIZE=$(jq -er '.[0].unpackedSize' "$PACK_JSON")
50
+ PACKAGE_TARBALL="$PACK_ROOT/$PACKAGE_FILENAME"
51
+ PACKAGE_SHA256=$(node -e "const crypto=require('node:crypto');const fs=require('node:fs');console.log(crypto.createHash('sha256').update(fs.readFileSync(process.argv[1])).digest('hex'))" "$PACKAGE_TARBALL")
52
+
53
+ jq -e '
54
+ (.[0].files | map(.path) | index("dist/cli/index.js")) != null and
55
+ (.[0].files | map(.path) | index("fixtures/v0.6/rule-corpus.ts")) != null and
56
+ (.[0].files | map(.path) | index("examples/github-action-sample/.github/workflows/aeoptimize.yml")) != null and
57
+ (.[0].files | map(.path) | index("scripts/verify-release-candidate.sh")) != null and
58
+ (.[0].files | map(.path) | index("scripts/verify-release-v0.6.sh")) != null
59
+ ' "$PACK_JSON" >/dev/null
60
+
61
+ npm_config_dry_run=false npm --cache "$VERIFY_ROOT/npm-cache" install \
62
+ --ignore-scripts --no-audit --no-fund \
63
+ --prefix "$CONSUMER_ROOT" "$PACKAGE_TARBALL" >/dev/null
64
+
65
+ for binary in aeoptimize aeo aeo-cli; do
66
+ BINARY_VERSION=$("$CONSUMER_ROOT/node_modules/.bin/$binary" --version)
67
+ if [ "$BINARY_VERSION" != "$PACKAGE_VERSION" ]; then
68
+ echo "$binary returned $BINARY_VERSION; expected $PACKAGE_VERSION" >&2
69
+ exit 1
70
+ fi
71
+ done
72
+
73
+ MANIFEST=$(jq -n \
74
+ --arg version "$PACKAGE_VERSION" \
75
+ --arg filename "$PACKAGE_FILENAME" \
76
+ --arg sha256 "$PACKAGE_SHA256" \
77
+ --argjson fileCount "$PACKAGE_FILE_COUNT" \
78
+ --argjson unpackedSize "$PACKAGE_UNPACKED_SIZE" \
79
+ '{version: $version, filename: $filename, sha256: $sha256, fileCount: $fileCount, unpackedSize: $unpackedSize}')
80
+
81
+ if [ -n "${RELEASE_MANIFEST_OUT:-}" ]; then
82
+ printf '%s\n' "$MANIFEST" > "$RELEASE_MANIFEST_OUT"
83
+ fi
84
+
85
+ printf '%s\n' "$MANIFEST"
86
+ echo "Release candidate checks passed."
@@ -2,14 +2,22 @@
2
2
  set -u
3
3
 
4
4
  PACKAGE_NAME=aeoptimize
5
- EXPECTED_VERSION=0.6.0
6
- EXPECTED_TAG=v0.6.0
7
5
  REPOSITORY=cucuwang/aeoptimize
8
6
  EXPECTED_COMMIT=${1:-}
9
7
  EXPECTED_PACKAGE_SHA256=${2:-}
10
8
  EXPECTED_REPOSITORY_URL=git+https://github.com/cucuwang/aeoptimize.git
11
9
  EXPECTED_HOMEPAGE=https://github.com/cucuwang/aeoptimize
12
10
  EXPECTED_BUGS_URL=https://github.com/cucuwang/aeoptimize/issues
11
+ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
12
+ PACKAGE_JSON="$SCRIPT_DIR/../package.json"
13
+
14
+ if ! command -v node >/dev/null 2>&1; then
15
+ echo "missing required command: node" >&2
16
+ exit 2
17
+ fi
18
+
19
+ EXPECTED_VERSION=$(node -e "const fs=require('node:fs');const packageJson=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));process.stdout.write(packageJson.version)" "$PACKAGE_JSON")
20
+ EXPECTED_TAG="v$EXPECTED_VERSION"
13
21
 
14
22
  if [ -z "$EXPECTED_COMMIT" ] || [ -z "$EXPECTED_PACKAGE_SHA256" ]; then
15
23
  echo "usage: $0 <expected-release-commit> <expected-package-sha256>" >&2
@@ -26,7 +34,7 @@ if ! [[ "$EXPECTED_PACKAGE_SHA256" =~ ^[0-9a-f]{64}$ ]]; then
26
34
  exit 2
27
35
  fi
28
36
 
29
- for command_name in awk curl jq npm git mktemp node; do
37
+ for command_name in awk curl jq npm git mktemp; do
30
38
  if ! command -v "$command_name" >/dev/null 2>&1; then
31
39
  echo "missing required command: $command_name" >&2
32
40
  exit 2
@@ -119,7 +127,7 @@ if curl -fsS "https://registry.npmjs.org/$PACKAGE_NAME" > "$REGISTRY_JSON"; then
119
127
 
120
128
  if npm --cache "$VERIFY_ROOT/npm-cache" install \
121
129
  --ignore-scripts --no-audit --no-fund \
122
- --prefix "$CONSUMER_ROOT" "$PACKAGE_NAME@$EXPECTED_VERSION" >/dev/null; then
130
+ --prefix "$CONSUMER_ROOT" "$PACKAGE_TARBALL" >/dev/null; then
123
131
  for binary in aeoptimize aeo aeo-cli; do
124
132
  binary_version=$("$CONSUMER_ROOT/node_modules/.bin/$binary" --version 2>/dev/null || true)
125
133
  if [ "$binary_version" = "$EXPECTED_VERSION" ]; then
@@ -129,7 +137,7 @@ if curl -fsS "https://registry.npmjs.org/$PACKAGE_NAME" > "$REGISTRY_JSON"; then
129
137
  fi
130
138
  done
131
139
  else
132
- fail "clean consumer installation failed for $PACKAGE_NAME@$EXPECTED_VERSION"
140
+ fail "clean consumer installation failed for the verified package tarball"
133
141
  fi
134
142
  else
135
143
  fail "npm does not contain exact version $EXPECTED_VERSION"