Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
8045502
Move editor helpers out of runtime assembly
wallstop Sep 9, 2026
405a27a
Automate validated package releases
wallstop Sep 9, 2026
2d08b4d
Fix editor window monitor fallback
wallstop Sep 9, 2026
f210599
Add runtime lifecycle PlayMode coverage
wallstop Sep 9, 2026
adc6855
Cover serialized runtime asset state
wallstop Sep 9, 2026
1784199
Defer processor discovery until needed
wallstop Sep 9, 2026
95d2f6e
Handle malformed user state JSON
wallstop Sep 9, 2026
5cfb856
Debounce splitter preference saves
wallstop Sep 9, 2026
7fc3949
Validate restored pane geometry
wallstop Sep 9, 2026
735103c
Suspend editor work during Play Mode
wallstop Sep 9, 2026
1de45af
Queue asset invalidation during Play Mode
wallstop Sep 9, 2026
5e81272
Cover missing saved asset selections
wallstop Sep 9, 2026
c97ab9f
Close Play Mode callback mutation paths
wallstop Sep 9, 2026
070ac60
Address release review findings
wallstop Sep 9, 2026
2f96c93
Cover persistence model round trips
wallstop Sep 9, 2026
6fe4cfc
Add reproducible benchmark driver
wallstop Sep 9, 2026
cca49fa
Harden benchmark editor lifecycle
wallstop Sep 9, 2026
4c737a7
Add Play Mode suspension matrix driver
wallstop Sep 9, 2026
bf1817f
Fix async resume pumping during Play Mode
wallstop Sep 9, 2026
3843989
Harden Play Mode scenario cleanup
wallstop Sep 9, 2026
6df52ce
Fix Play Mode mutation resume predicate
wallstop Sep 9, 2026
27d0964
Exercise script reload during Play Mode
wallstop Sep 9, 2026
017dac1
Measure independent play entry benchmark cases
wallstop Sep 9, 2026
f3a7fa8
Verify destroy cleanup during Play Mode
wallstop Sep 9, 2026
3200d0b
Make benchmark fixture generation cooperative
wallstop Sep 10, 2026
08e3bd5
Cover large benchmark bootstrap batching
wallstop Sep 10, 2026
6cfbcd2
Add benchmark matrix harness
wallstop Sep 10, 2026
ef80b56
Validate benchmark fixture lifecycle evidence
wallstop Sep 10, 2026
c5990aa
Harden benchmark batching and matrix evidence
wallstop Sep 10, 2026
9f2727c
Add window-independent editor automation facade
wallstop Sep 10, 2026
06518b9
Harden automation asset query
wallstop Sep 10, 2026
f407981
Harden automation settings handling
wallstop Sep 10, 2026
b8bfbb0
Preserve live automation persistence state
wallstop Sep 10, 2026
cd964ab
Add explicit automation asset operations
wallstop Sep 10, 2026
589244f
Add versioned automation JSON adapter
wallstop Sep 10, 2026
e3b5336
Validate automation operation destinations
wallstop Sep 10, 2026
2777994
Emit structured automation runner failures
wallstop Sep 10, 2026
d8e0cc6
Add strict documentation site
wallstop Sep 10, 2026
6b0431f
Harden benchmark and automation contracts
wallstop Sep 10, 2026
288bad2
Isolate documentation deployment concurrency
wallstop Sep 10, 2026
62bd8d1
Harden automation file execution
wallstop Sep 10, 2026
c400281
Validate documentation capture inventory
wallstop Sep 10, 2026
7d6b59f
Extend benchmark evidence and asset automation
wallstop Sep 10, 2026
6f05fb1
Add serialized property automation operations
wallstop Sep 10, 2026
12213cc
Structure benchmark control matrix
wallstop Sep 10, 2026
5d21781
Harden benchmark recovery ownership
wallstop Sep 10, 2026
a56e02c
Expose index progress through automation API
wallstop Sep 10, 2026
11d5588
Harden index progress refresh contract
wallstop Sep 10, 2026
ec3bc2e
Harden automation failure boundaries
wallstop Sep 10, 2026
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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
###############################################################################
*.sh text eol=lf

# CSharpier follows the repository .editorconfig for C# files.
*.cs text eol=crlf

# Generated Markdown is compared byte-for-byte by the cross-platform harness.
.llm/skills/index.md text eol=lf

Expand Down
36 changes: 36 additions & 0 deletions .github/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Release operations

Releases are prepared from `main` with the **Prepare release** workflow. Select
`patch`, `minor`, or `major`, or choose `none` and provide an explicit SemVer.
Use `dry_run` first. A real run updates `package.json`, `.llm/context.md`, and
`CHANGELOG.md`, then opens one `release/vX.Y.Z` pull request.

## Required repository configuration

- `AUTO_COMMIT_APP_ID`: the numeric ID of a GitHub App installed on this
repository.
- `AUTO_COMMIT_APP_PRIVATE_KEY`: the PEM private key for that App.
- The App installation must have only `Contents: write` and `Pull requests:
write` for this repository. The tag workflow uses only `Contents: write`.
- npm Trusted Publishing must be configured for
`com.wallstop-studios.data-visualizer` with this repository, the
`Publish release artifacts` workflow, and the default branch/tag workflow
identity. No npm token secret is used.

## Recovery and reruns

The tag workflow validates the merged release branch and creates an annotated
`vX.Y.Z` tag. An existing tag is never retargeted automatically: a tag at the
same commit is left alone, while a tag at another commit fails closed.

The publication workflow checks whether the exact npm `name@version` already
exists and skips republishing it. It always validates the checked-out tag and
artifacts first. If npm succeeds but GitHub Release creation fails, rerun the
tag workflow from the existing tag event or use the workflow run again; release
creation updates an existing release and replaces attachments by checksum-safe
name. A failed release-preparation run may be retried only after resolving an
existing branch or PR with the same version.

All `.unitypackage` assembly and validation uses Python, tar, gzip, and checksum
tools in cloud CI. Unity is not installed, authenticated, or invoked by any
release workflow; EditMode and PlayMode validation remain local gates.
70 changes: 70 additions & 0 deletions .github/workflows/docs-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Documentation Pages

on:
push:
branches: [main]
paths:
- "docs/**"
- "mkdocs.yml"
- "requirements-docs.txt"
- "scripts/docs/**"
- ".github/workflows/docs-pages.yml"
pull_request:
paths:
- "docs/**"
- "mkdocs.yml"
- "requirements-docs.txt"
- "scripts/docs/**"
- ".github/workflows/docs-pages.yml"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install documentation dependencies
run: python -m pip install --requirement requirements-docs.txt

- name: Validate documentation graph and examples
run: python scripts/docs/validate_docs.py

- name: Build strict site
run: mkdocs build --strict --clean --site-dir site

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: site

deploy:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Configure Pages
uses: actions/configure-pages@v5
- name: Deploy Pages artifact
id: deployment
uses: actions/deploy-pages@v4
192 changes: 132 additions & 60 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -1,100 +1,172 @@
name: Publish to NPM
name: Publish release artifacts

# Manual, on-demand publishing: dispatch this workflow to publish the current package.json
# version. The publish step is re-runnable (it skips a version already on the registry) and
# uses npm Trusted Publishing through GitHub OIDC.
# A tag created by release-tag.yml is the only publication trigger. The tag is
# created with a GitHub App token so this event is not suppressed by GitHub's
# GITHUB_TOKEN recursion protection.
on:
workflow_dispatch:
inputs:
dry_run:
description: "Pack and report the version/dist-tag without publishing"
type: boolean
default: false
push:
tags:
- "v*"

concurrency:
group: publish-npm
group: publish-${{ github.ref_name }}
cancel-in-progress: false

permissions:
contents: read

jobs:
publish:
name: Publish npm package
package:
name: Validate and package release
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
id-token: write # required for npm Trusted Publishing through OIDC
timeout-minutes: 20
steps:
- name: Checkout
- name: Checkout exact tag
uses: actions/checkout@v7
with:
ref: ${{ github.ref }}
fetch-depth: 0
persist-credentials: false

- name: Set up Node.js
uses: actions/setup-node@v7
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false

- name: Pack and resolve version / dist-tag
id: pack
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Validate release metadata
id: release
run: |
set -euo pipefail
pkg="$(jq -r '.name' package.json)"
ver="$(jq -r '.version' package.json)"
if [ -z "${pkg}" ] || [ "${pkg}" = "null" ] || [ -z "${ver}" ] || [ "${ver}" = "null" ]; then
echo "::error::package.json is missing name or version."
version="${GITHUB_REF_NAME#v}"
python3 scripts/release/release_tools.py validate-release --root . --version "${version}"
tag="v${version}"
if [ "$(git describe --tags --exact-match HEAD 2>/dev/null || true)" != "${tag}" ]; then
echo "::error::Checked-out commit is not the exact release tag ${tag}."
exit 1
fi
# rc/alpha/beta/preview pre-releases publish under the "next" dist-tag; stable under "latest".
if printf '%s' "${ver}" | grep -qiE '\-(rc|alpha|beta|preview)'; then
npm_tag="next"
else
npm_tag="latest"
fi
package_file="$(npm pack --json | jq -r '.[0].filename')"
{
echo "pkg=${pkg}"
echo "ver=${ver}"
echo "npm_tag=${npm_tag}"
echo "package_file=${package_file}"
} >> "${GITHUB_OUTPUT}"
echo "Prepared ${pkg}@${ver} -> dist-tag '${npm_tag}' (${package_file})."

- name: Verify npm trusted publishing toolchain
echo "version=${version}" >> "${GITHUB_OUTPUT}"

- name: Run non-Unity repository checks
run: |
set -euo pipefail
npm run lint:llm
pwsh -NoProfile -File scripts/tests/run-all.ps1

- name: Pack and verify npm payload
id: npm
env:
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
node -e 'const p=require("./package.json"); if (p.version!==process.env.VERSION || p.name!=="com.wallstop-studios.data-visualizer") process.exit(1);'
npm pack --json --pack-destination "${RUNNER_TEMP}" > "${RUNNER_TEMP}/npm-pack.json"
package_file="${RUNNER_TEMP}/$(jq -r '.[0].filename' "${RUNNER_TEMP}/npm-pack.json")"
[ -f "${package_file}" ]
while IFS= read -r member; do
case "${member}" in
package/Editor|package/Editor.meta|package/Editor/*|package/Runtime|package/Runtime.meta|package/Runtime/*|package/Tests|package/Tests.meta|package/Tests/*|package/docs|package/docs.meta|package/docs/*|package/CHANGELOG.md|package/CHANGELOG.md.meta|package/README.md|package/README.md.meta|package/LICENSE|package/LICENSE.meta|package/package.json|package/package.json.meta) ;;
*) echo "::error::Unexpected npm payload member ${member}"; exit 1 ;;
esac
done < <(tar -tzf "${package_file}" | sed 's#/$##')
sha256sum "${package_file}" > "${package_file}.sha256"
echo "file=${package_file}" >> "${GITHUB_OUTPUT}"

- name: Build and validate Unity package
id: unity
env:
VERSION: ${{ steps.release.outputs.version }}
run: |
set -euo pipefail
output="${RUNNER_TEMP}/com.wallstop-studios.data-visualizer-${VERSION}.unitypackage"
python3 scripts/release/release_tools.py build-unitypackage --root . --output "${output}"
python3 scripts/release/release_tools.py validate-unitypackage --archive "${output}" --checksum "${output}.sha256" --root .
echo "file=${output}" >> "${GITHUB_OUTPUT}"

- name: Upload validated release artifacts
uses: actions/upload-artifact@v4
with:
name: release-${{ github.ref_name }}
if-no-files-found: error
path: |
${{ steps.npm.outputs.file }}
${{ steps.npm.outputs.file }}.sha256
${{ steps.unity.outputs.file }}
${{ steps.unity.outputs.file }}.sha256

publish:
name: Publish npm and GitHub Release
needs: package
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: write
id-token: write
steps:
- name: Checkout exact tag
uses: actions/checkout@v7
with:
ref: ${{ github.ref }}
fetch-depth: 0
persist-credentials: false

- name: Set up Node.js for trusted publishing
uses: actions/setup-node@v7
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Download validated artifacts
uses: actions/download-artifact@v4
with:
name: release-${{ github.ref_name }}
path: "${{ runner.temp }}/release"

- name: Publish exact npm version
env:
VERSION: ${{ github.ref_name }}
run: |
set -euo pipefail
node --version
npm --version
version="${VERSION#v}"
package_file="$(find "${RUNNER_TEMP}/release" -maxdepth 1 -name '*.tgz' -print -quit)"
[ -n "${package_file}" ]
required_npm="11.5.1"
current_npm="$(npm --version)"
if [ "$(printf '%s\n' "${required_npm}" "${current_npm}" | sort -V | head -n1)" != "${required_npm}" ]; then
echo "::error::npm ${current_npm} is too old; trusted publishing requires npm >= ${required_npm}."
exit 1
fi
pkg="$(node -p 'require("./package.json").name')"
if npm view "${pkg}@${version}" version >/dev/null 2>&1; then
echo "${pkg}@${version} already exists; skipping irreversible publish."
else
npm_tag="$(python3 -c 'import sys; sys.path.insert(0, "scripts/release"); from release_tools import npm_dist_tag; print(npm_dist_tag(sys.argv[1]))' "${version}")"
npm publish "${package_file}" --access public --tag "${npm_tag}"
fi

- name: Publish to npm with trusted publishing
if: ${{ inputs.dry_run == false }}
- name: Create or update GitHub Release
env:
PKG: ${{ steps.pack.outputs.pkg }}
VER: ${{ steps.pack.outputs.ver }}
NPM_TAG: ${{ steps.pack.outputs.npm_tag }}
PACKAGE_FILE: ${{ steps.pack.outputs.package_file }}
TAG: ${{ github.ref_name }}
run: |
set -euo pipefail
# Re-runnable: skip if this exact name@version is already on the registry (a prior run may
# have published it before failing downstream). npm publish is otherwise irreversible.
if npm view "${PKG}@${VER}" version >/dev/null 2>&1; then
echo "::notice::${PKG}@${VER} is already on the registry; skipping publish."
version="${TAG#v}"
notes="$(python3 scripts/release/release_tools.py changelog-section --root . --version "${version}" | jq -r .section)"
if python3 -c 'import sys; sys.path.insert(0, "scripts/release"); from release_tools import parse_version; print(bool(parse_version(sys.argv[1])[3]))' "${version}" | grep -qx true; then prerelease_flag=--prerelease; else prerelease_flag=; fi
files=("${RUNNER_TEMP}/release"/*)
if gh release view "${TAG}" >/dev/null 2>&1; then
gh release edit "${TAG}" --title "${TAG}" --notes "${notes}" ${prerelease_flag}
gh release upload "${TAG}" "${files[@]}" --clobber
else
npm publish "${PACKAGE_FILE}" --access public --tag "${NPM_TAG}"
echo "::notice::Published ${PKG}@${VER} to dist-tag '${NPM_TAG}'."
gh release create "${TAG}" "${files[@]}" --title "${TAG}" --notes "${notes}" ${prerelease_flag}
fi

- name: Dry-run summary
if: ${{ inputs.dry_run == true }}
run: |
echo "Dry run: would publish ${{ steps.pack.outputs.pkg }}@${{ steps.pack.outputs.ver }} to dist-tag '${{ steps.pack.outputs.npm_tag }}'."
Loading
Loading