Skip to content
This repository was archived by the owner on Aug 7, 2026. It is now read-only.
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Binary file modified .DS_Store
Binary file not shown.
10 changes: 10 additions & 0 deletions .agents/skills/determinism-validation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: determinism-validation
description: "Validate deterministic GameMaker gameplay before and after changes that touch seeded RNG ownership or consumption, timing or step order, replayability, wave/spawn/score/rank/progression/entity sequencing, save/load continuity, initialization order, or structural extraction dependent on those contracts. Do not use merely because a file is large or ordinary tests exist."
---

# Determinism Validation

Define the deterministic contract and scope before selecting tests. Read [the validation workflow](references/determinism-workflow.md), then use the smallest existing project facilities that can expose divergence.

Follow `docs/VALIDATION.md` for authoritative commands and acceptance criteria. Do not change production behavior to simplify a test, and do not treat a compile or the known unavailable local runner as a runtime pass.
6 changes: 6 additions & 0 deletions .agents/skills/determinism-validation/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface:
display_name: "Determinism Validation"
short_description: "Verify deterministic gameplay state changes"
default_prompt: "Use $determinism-validation to validate deterministic behavior after this change."
policy:
allow_implicit_invocation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Determinism workflow

1. State the contract being preserved and define comparable checkpoints. Identify the run inputs, initial state, relevant state fields, and expected events.
2. Identify every relevant RNG source, seed, owner, and consumption order. Do not add, remove, or move a random draw without treating it as a contract change.
3. Identify the timing source, Begin/Step/End Step/Draw ordering, initialization order, and frame-sensitive behavior.
4. Select the smallest applicable existing test or harness. Use focused GMTL coverage first; use project-owned test helpers or visual-tour facilities only when their evidence fits the contract.
5. Compare checkpoint state using existing project facilities where available. Compare applicable events: spawns; entity creation/destruction; score/rank changes; wave transitions; drops; boss/phase transitions; save/load restoration; and replay results.
6. When divergence occurs, report the first divergent frame, event, state field, or RNG-consumption point that can be established. Record unknowns rather than inferring equivalence.
7. Distinguish successful compilation from a runtime test. For a full GMTL pass, require both documented summary lines and every declared test to pass; follow `docs/VALIDATION.md` for the current command and criteria.
8. Never describe the known local runner failure as a pass. When local runtime execution remains unavailable, use the documented hosted Windows validation fallback in `docs/VALIDATION.md`.
9. Do not change production behavior merely to make validation easier. Explicitly record behavior that could not be verified.
10 changes: 10 additions & 0 deletions .agents/skills/gamemaker-resource-change/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: gamemaker-resource-change
description: "Safely create, register, move, rename, or reorganize GameMaker resources and their `.yy`, `.yyp`, and Asset Browser folder metadata; also use for orphaned or unregistered resource checks and a dedicated IDE organization pass. Do not use for ordinary edits inside an existing `.gml` file when no resource metadata changes."
---

# GameMaker Resource Change

Read [the metadata workflow](references/metadata-workflow.md) before touching a resource definition. Follow the repository’s established `.yy`, `.yyp`, and folder conventions; physical directories and IDE folders are separate concerns.

Use documented terminal/headless validation from `docs/VALIDATION.md`. Do not launch GameMaker, Igor, or the runner unless the task clearly requires and authorizes it. Preserve unrelated work, including the existing `.DS_Store` change.
6 changes: 6 additions & 0 deletions .agents/skills/gamemaker-resource-change/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface:
display_name: "GameMaker Resource Change"
short_description: "Safely maintain GameMaker resource metadata"
default_prompt: "Use $gamemaker-resource-change to add and register this GameMaker resource."
policy:
allow_implicit_invocation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# GameMaker metadata workflow

1. Identify every affected implementation or binary, matching `.yy`, project entry in `Selkie's Moon ~ until we meet again ~/Selkies Moon.yyp`, and applicable `folders/*.yy` metadata before editing.
2. Preserve resource identities and all existing references. Avoid unnecessary renames; do not reorganize unrelated resources during a bounded code extraction.
3. Register a new resource in the same `.yyp` style as comparable resources and assign an appropriate existing or deliberately created Asset Browser folder. IDE folders do not need to mirror physical directories.
4. Prefer the project’s existing folder taxonomy over inventing one. Treat a broad Asset Browser cleanup as its own dedicated task.
5. Check the affected scope for orphaned `.yy` resources, missing or duplicate `.yyp` registrations, invalid resource paths, and stale folder references. Review the diff for accidental IDE metadata churn.
6. Validate metadata and compilation with the documented tier in `docs/VALIDATION.md`; use terminal and headless checks by default. Do not launch GameMaker, Igor, or the runner unless the task expressly authorizes it.
7. Preserve pre-existing work, including `.DS_Store`.

Do not add a metadata-integrity checker automatically. If repeated manual checks cannot establish these invariants and no existing repository tool covers them, first propose a small deterministic checker with its inputs, checks, output, and failure behavior; add it only with task approval and without duplicating an existing tool.
12 changes: 12 additions & 0 deletions .agents/skills/structural-decomposition/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
name: structural-decomposition
description: "Plan and carry out behavior-preserving decomposition of an oversized, multi-responsibility, or highly coupled GameMaker production owner. Use for responsibility audits, one cohesive extraction, caller migration through a compatibility facade, facade retirement, or durable module-ownership updates. Do not use for gameplay or balance work, broad cleanup, arbitrary line-count reduction, renaming-only work, or localized fixes that do not change ownership."
---

# Structural Decomposition

Start from the named owner and its routed scope in `docs/ARCHITECTURE.md`; preserve unrelated work. Read [the structural audit](references/structural-audit.md) before selecting an extraction.

Use [characterization-first extraction](references/characterization-first-extraction.md) for the selected responsibility, [compatibility facades](references/compatibility-facades.md) whenever callers cannot migrate atomically, and [milestones and reporting](references/milestones-and-reporting.md) to close the approved milestone.

Route new, moved, or reorganized GameMaker resources through `$gamemaker-resource-change`. Route RNG, timing, replay, save/load, or state-equivalence concerns through `$determinism-validation`. Stop when the approved milestone is complete.
6 changes: 6 additions & 0 deletions .agents/skills/structural-decomposition/agents/openai.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface:
display_name: "Structural Decomposition"
short_description: "Extract cohesive GameMaker modules safely"
default_prompt: "Use $structural-decomposition to plan a behavior-preserving extraction."
policy:
allow_implicit_invocation: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Characterization-first extraction

1. Establish focused characterization coverage before moving behavior whose contract is not already verified. Prefer existing focused GMTL tests; add the smallest contract test when needed.
2. Move one coherent cluster per milestone. Keep object events orchestration-focused and place shared behavior in the routed owner.
3. Preserve observable behavior, balance, save formats, messages, initialization order, event/timing semantics, and RNG consumption order unless the approved task explicitly changes and tests that contract.
4. Keep the new interface narrow and explicit. Avoid broad renaming, formatting churn, duplicate ownership, and unrelated cleanup.
5. If the extraction creates, moves, or registers a GameMaker resource, invoke `$gamemaker-resource-change` before changing metadata.
6. Run focused validation after each meaningful extraction. Use the full relevant validation tier before completion, following `docs/VALIDATION.md`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Compatibility-facade lifecycle

Use a forwarding wrapper when callers cannot safely migrate together or when a public contract needs a stable seam.

1. Keep the original public entry point and forward to the new narrow owner without changing arguments, return values, side effects, timing, or RNG use.
2. Migrate intended callers in a bounded milestone and verify that the facade and new owner do not contain duplicated implementation.
3. Remove the old implementation only after intended callers use the new owner and characterization coverage passes.
4. Retire the facade only after its remaining callers are deliberately migrated and no external contract requires it. Treat facade removal as its own reviewable milestone when it is not atomic with the extraction.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Milestone completion and report

Before closing a milestone:

- Confirm exactly one responsibility cluster moved and the approved stop point was reached.
- Confirm intended callers, state ownership, initialization dependencies, and compatibility wrappers are accounted for.
- Run focused validation after extraction and the full relevant tier before completion; record commands and results.
- Update `docs/ARCHITECTURE.md`, module mapping, `docs/PROJECT_STATE.md`, or a handoff only when a durable ownership, supported-state, or continuity fact changed. Follow `docs/HANDOFF_TEMPLATE.md` for an interrupted non-obvious state.

Final report format:

1. Target and extracted responsibility.
2. Public-contract/caller migration and any remaining facade.
3. Behavior-sensitive contracts checked, including a `$determinism-validation` result when applicable.
4. Validation evidence and unverified behavior.
5. Durable documentation or handoff updates, if any.
6. Explicit stop point and follow-up extraction, if any.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Structural audit

1. Start with the named target, its `/// @func` contracts, direct callers, adjacent tests, and the matching `docs/ARCHITECTURE.md` route. Expand only for a demonstrated dependency.
2. Inventory public functions, direct callers, externally accessed instance/global/struct state, side effects, initialization dependencies, object-event ordering, and required GameMaker resources. Record the source and destination owner for each candidate responsibility.
3. Cluster behavior by cohesive responsibility, data ownership, and dependency direction. Do not select work merely to shorten a file or because adjacent functions have similar names.
4. Mark compatibility-sensitive contracts: gameplay behavior and balance, save formats, message contracts, initialization order, timing semantics, and deterministic RNG consumption. Invoke `$determinism-validation` for the applicable contracts.
5. Choose one independently reviewable responsibility cluster. Define its narrow interface, expected callers, validation, and a stop point before editing.

Keep the audit in the task or handoff; update architecture ownership only once the result is durable.
110 changes: 110 additions & 0 deletions .github/workflows/gamemaker-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: GameMaker unit tests

on:
pull_request:
branches:
- dev
push:
branches:
- dev
workflow_dispatch:

permissions:
contents: read

concurrency:
group: gamemaker-unit-tests-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
governance:
name: Governance consistency
runs-on: ubuntu-24.04
timeout-minutes: 5

steps:
- name: Check out repository
uses: actions/checkout@v7.0.0

- name: Check governance contracts
run: python3 tools/check_governance.py

gmtl:
name: GMTL unit tests
runs-on: windows-2022
timeout-minutes: 30
# GitHub does not expose repository secrets to pull requests from forks.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository

steps:
- name: Check out repository
uses: actions/checkout@v7.0.0

- name: Require GameMaker CI access key
shell: pwsh
env:
ACCESS_KEY: ${{ secrets.ACCESS_KEY }}
run: |
if ([string]::IsNullOrWhiteSpace($env:ACCESS_KEY)) {
throw "Add the GameMaker access key as the repository Actions secret ACCESS_KEY."
}

- name: Set up GameMaker Igor
id: igor
uses: bscotch/igor-setup@b7126413204dfd85b2e26c6276940ee78d70cb29
with:
access-key: ${{ secrets.ACCESS_KEY }}
runtime-version: 2026.0.0.23
modules: windows
cache: true

# igor-build invokes Igor through a shell that does not preserve spaces
# in /uf, so hand it an equivalent user directory with a safe path.
- name: Prepare space-safe GameMaker user directory
id: game_maker_user
shell: pwsh
env:
SOURCE_USER_DIR: ${{ steps.igor.outputs.user-dir }}
run: |
$safeUserDirectory = Join-Path $env:RUNNER_TEMP 'gm-user'
New-Item -ItemType Directory -Force -Path $safeUserDirectory | Out-Null
Get-ChildItem -LiteralPath $env:SOURCE_USER_DIR -Force | ForEach-Object {
Copy-Item -LiteralPath $_.FullName `
-Destination $safeUserDirectory `
-Recurse `
-Force
}
"path=$safeUserDirectory" >> $env:GITHUB_OUTPUT

- name: Build Windows VM test runner
id: build
uses: bscotch/igor-build@v1.3.3
with:
user-dir: ${{ steps.game_maker_user.outputs.path }}
yyp-path: ${{ github.workspace }}\Selkie's Moon ~ until we meet again ~\Selkies Moon.yyp
platform: windows
config: Default
yyc: false
name: SelkiesMoonTests.zip
save-logs: true

- name: Run GMTL unit tests
shell: pwsh
run: |
./tools/run_gmtl_tests_ci.ps1 `
-BuildDirectory '${{ steps.build.outputs.out-dir }}' `
-BuildName '${{ steps.build.outputs.out-name }}'

- name: Upload test and compiler logs
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: gamemaker-unit-test-logs
path: |
test-results/github-actions-gmtl.log
test-results/github-actions-gmtl-stdout.log
test-results/github-actions-gmtl-stderr.log
test-results/github-actions-game/debug.log
${{ steps.build.outputs.log-dir }}
if-no-files-found: ignore
retention-days: 14
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/.DS_Store
.DS_Store
/cache/
/output/
/test-results/
/.DS_Store
/tmp/
/art/audio_production/raw_bounces/
/art/audio_production/superseded/
/tools/__pycache__/
67 changes: 67 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Repository Guidance

This file contains only Selkie's Moon rules. Higher-priority platform and user
instructions still apply.

## Authority and cold start

Use this order when repository sources disagree:

1. the active task and this file for scope and process;
2. [`docs/PROJECT_STATE.md`](docs/PROJECT_STATE.md) for the current supported and
incomplete state;
3. [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) for subsystem ownership and task
routing;
4. the relevant subsystem guide for behavior and data contracts;
5. project-owned `/// @func` contracts, executable code, and tests for the
implemented behavior.

Treat a code/document disagreement as drift to resolve, not permission to
silently choose whichever source is convenient.

For a new task:

1. read `docs/PROJECT_STATE.md`;
2. read only the matching row and linked section in `docs/ARCHITECTURE.md`;
3. inspect `git status --short` and preserve unrelated work;
4. open the named owner files and their focused tests.

Do not read every guide or enumerate the full resource tree during onboarding.

## Scope boundaries

- The GameMaker project is
`Selkie's Moon ~ until we meet again ~/Selkies Moon.yyp`.
- New resources need the implementation file, matching `.yy`, and `.yyp`
registration. Review metadata diffs for accidental IDE churn.
- Do not hand-edit binary image, audio, Krita, Blender, or archive data.
- Keep object events orchestration-focused; shared behavior belongs in the
owning script named by `docs/ARCHITECTURE.md`.
- Preserve persistence through explicit versioned migrations and tests.
- `GMTL_*` scripts are vendored test-library code and are out of normal refactor
scope.

Normally ignore `.git/`, `output/`, `cache/`, `test-results/`, `tmp/`, and
platform build products. For code tasks, also avoid `art/`, `sprites/`,
`sounds/`, and generated source manifests unless the routed owner or task
requires them.

## Validation and continuity

Choose the smallest sufficient validation tier from
[`docs/VALIDATION.md`](docs/VALIDATION.md). Every governance or documentation
change must run:

```zsh
python3 tools/check_governance.py
git diff --check
```

Runtime changes normally require the full GMTL suite. Asset and release checks
are task-specific; do not launch local GUI tooling merely to inspect structure.

A completed commit with explicit validation is the normal durable handoff. If
work must stop in a non-obvious state, create a task-specific note from
[`docs/HANDOFF_TEMPLATE.md`](docs/HANDOFF_TEMPLATE.md); do not turn
`PROJECT_STATE.md` into a chronological task log. Update `PROJECT_STATE.md` only
when a durable supported feature, known gap, owner, or validation fact changes.
Loading