From 59a62b5fe07fc3f186d73265b11a6fe4ccc3fc96 Mon Sep 17 00:00:00 2001 From: ShugokiFable <35711149+ShugokiFable@users.noreply.github.com> Date: Wed, 26 Aug 2026 22:09:00 -0300 Subject: [PATCH] Sync standalone mirror to Skyrim Forge 6.0.0 The bundle repository has been Forge's development home since 5.2.1; this mirror had drifted a full major behind. Sync it to the 6.0.0 source that actually ships. - Version identity 5.2.1 -> 6.0.0 across VERSION.txt, CURRENT.txt, skyrim_forge/version.py, pyproject.toml and writer/native-go/main.go, with the native helpers rebuilt under the pinned go1.23.2 and CHECKSUMS updated. - Remove `forge bundle-contract` and its tests, as 6.0.0 did. One repository cannot usefully negotiate a version range with itself, and the range it carried had already gone stale: it declared support for bundle >=7.8.0,<8.0.0 and so returned FAIL for every bundle 8.x, including the current 8.6.5. - Add tests/_workflows.py. Tests that assert something about CI now resolve the nearest enclosing .github/workflows instead of hardcoding a path, so the same test file is correct in this repository and in the bundle subtree. In this repository it resolves to .github/workflows on the first iteration, so nothing skips; verified, no workflow test reports a skip here. - Keep .github/ and .gitattributes, which exist only in this repository. Forge's own workflows are deleted inside the bundle because GitHub reads workflows only from a repository root; here this IS the root, so they run. - README describes a mirror rather than a repository frozen at 5.2.1, and the install steps match 6.0.0's launcher, which installs on a bare double-click and keeps the maintenance menu behind --menu. Line endings were renormalized through the index before the integrity reports were regenerated. MANIFEST.json hashes what a fresh checkout produces, and .gitattributes forces CRLF for .ps1/.bat here while the bundle stores them LF under `* -text`; hashing the copied working tree directly is what broke v5.1.7. Full gate: python scripts/validate_repository.py --scope full -> PASS, 159 tests OK (4 environmental skips). Co-Authored-By: Claude Opus 5 --- BUILD-RECEIPT.json | 32 ++--- CHANGELOG.md | 27 ++++- CHANGELOG.txt | 7 ++ CHECKSUMS-SHA256.txt | 4 +- CURRENT.txt | 2 +- Install-or-Update.ps1 | 16 ++- MANIFEST.json | 90 +++++++-------- README.md | 64 +++++----- Register-MCP.ps1 | 109 ++++++++++++++++-- SBOM.spdx.json | 81 ++++++------- START-HERE.bat | 60 +++++++++- VALIDATION.json | 77 ++++++------- VERSION.txt | 4 +- integrations/skyrim-forge/SKILL.md | 61 ++++++++-- pyproject.toml | 2 +- scripts/rebuild_native_helpers.py | 9 +- scripts/validate_repository.py | 46 ++++++-- skyrim_forge/bin/linux-x64/SkyrimForge.Native | Bin 1839239 -> 1839239 bytes .../bin/win-x64/SkyrimForge.Native.exe | Bin 1955328 -> 1955328 bytes skyrim_forge/bundle_contract.py | 59 ---------- skyrim_forge/cli.py | 6 - skyrim_forge/version.py | 2 +- tests/_workflows.py | 27 +++++ tests/test_bundle_contract_v520.py | 59 ---------- tests/test_ci_validation.py | 37 +++++- tests/test_powershell_release.py | 26 ++++- tests/test_windows_provider_bridge.py | 82 +++---------- writer/native-go/main.go | 2 +- writer/published/linux-x64/SkyrimForge.Native | Bin 1839239 -> 1839239 bytes .../published/win-x64/SkyrimForge.Native.exe | Bin 1955328 -> 1955328 bytes 30 files changed, 571 insertions(+), 420 deletions(-) delete mode 100644 skyrim_forge/bundle_contract.py create mode 100644 tests/_workflows.py delete mode 100644 tests/test_bundle_contract_v520.py diff --git a/BUILD-RECEIPT.json b/BUILD-RECEIPT.json index c0be426..78d4f0d 100644 --- a/BUILD-RECEIPT.json +++ b/BUILD-RECEIPT.json @@ -3,7 +3,7 @@ "builds": { "linux": { "buildvcs": false, - "bundled": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "bundled": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "bundled_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", @@ -15,24 +15,24 @@ "stderr": "", "stdout": "\\writer\\published\\linux-x64\\SkyrimForge.Native: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" }, - "first": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "first": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "first_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", "version", "-m", - "\\AppData\\Local\\Temp\\tmp6e19rlye\\linux-a" + "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\linux-a" ], "returncode": 0, "stderr": "", - "stdout": "\\AppData\\Local\\Temp\\tmp6e19rlye\\linux-a: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" + "stdout": "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\linux-a: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" }, "result": "PASS", - "second": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb" + "second": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73" }, "windows": { "buildvcs": false, - "bundled": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "bundled": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "bundled_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", @@ -44,20 +44,20 @@ "stderr": "", "stdout": "\\writer\\published\\win-x64\\SkyrimForge.Native.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" }, - "first": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "first": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "first_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", "version", "-m", - "\\AppData\\Local\\Temp\\tmp6e19rlye\\windows-a.exe" + "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\windows-a.exe" ], "returncode": 0, "stderr": "", - "stdout": "\\AppData\\Local\\Temp\\tmp6e19rlye\\windows-a.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" + "stdout": "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\windows-a.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" }, "result": "PASS", - "second": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "second": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" } }, "format": { @@ -154,14 +154,14 @@ "native": { "executed_platform": "windows", "hashes": { - "linux": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", - "windows": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "linux": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", + "windows": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" }, "linux_elf": true, "package_copies_match": true, "package_copy_hashes": { - "linux": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", - "windows": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "linux": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", + "windows": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" }, "result": "PASS", "self_test": { @@ -180,11 +180,11 @@ ], "returncode": 0, "stderr": "", - "stdout": "SkyrimForge.Native 5.2.1 go\n" + "stdout": "SkyrimForge.Native 6.0.0 go\n" }, "windows_pe": true }, "product": "Skyrim Forge", "result": "PASS", - "version": "5.2.1" + "version": "6.0.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index aa97dec..717635b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # Changelog +## 6.0.0 + +Skyrim Forge is developed inside the **Ultimate AI Starter Bundle** repository +now, at `BUNDLED-TOOLS/skyrim-forge`, and is no longer released separately. The +split is what allowed bundle 7.8.0 to call a contract field Forge has never +emitted: two files that had to agree, in two repositories, with no single commit +that could test both. One repository, one commit, one gate. + +- **Removed `forge bundle-contract`.** It negotiated a supported bundle version + range, which was only ever needed because the two shipped from separate repos + on separate schedules. Shipping together, that check cannot fail for a real + reason -- but it can go stale and start rejecting the very bundle it lives + inside, which is one edit away from what already happened. The bundle + installer runs `forge doctor` instead, which can still genuinely fail: broken + virtualenv, missing native helper, unreadable config, unresolvable workspace. +- **The version gate looks for CI at the checkout root.** GitHub reads + `.github/workflows` only from a repository root, so this subtree carries no + workflows of its own any more. The gate walks up to the checkout root and + scans every workflow it finds, instead of opening one remembered filename -- + which would now be a file that no longer exists. +- **The install directory is unchanged and still carries no version.** It is + `Skyrim-Forge`; the bundle installer migrates a version-stamped install onto + that name. +- Native helpers rebuilt reproducibly as 6.0.0 under pinned Go 1.23.2. + ## 5.2.1 - **`forge --help`, the GUI title and the Go self-test fixture all announced the @@ -21,7 +46,7 @@ Compatibility and one-click integration release for Ultimate AI Starter Bundle 7 - **Machine-readable bundle handshake.** `forge bundle-contract --bundle-version X.Y.Z` now proves whether this Forge build and a bundle can safely cooperate. Forge 5.2 - accepts bundle `>=7.8.0,<8.0.0`, returns capabilities as JSON, and uses a failing + accepts the legacy 7.8.x bundle range, returns capabilities as JSON, and uses a failing process exit code for an incompatible contract. - **Provider registration remains self-verifying.** The 5.1.7 Claude Desktop support is retained alongside Claude Code, Codex, Grok, Kimi, and Hermes registration. diff --git a/CHANGELOG.txt b/CHANGELOG.txt index a8f4f28..44544eb 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,10 @@ +Skyrim Forge 6.0.0 + +- Developed inside the Ultimate AI Starter Bundle repository at BUNDLED-TOOLS/skyrim-forge; no separate release. +- Removed `forge bundle-contract`: a version handshake between two halves of one repository can only go stale, never usefully fail. The bundle installer runs `forge doctor` instead. +- The version gate finds CI at the checkout root instead of opening a workflow path this subtree no longer has. +- Native helpers rebuilt reproducibly as 6.0.0 with pinned Go 1.23.2. + Skyrim Forge 5.2.1 - forge --help, the GUI title and the Go self-test fixture announced the 4.2 series from a 5.2.0 install; all three now interpolate VERSION. diff --git a/CHECKSUMS-SHA256.txt b/CHECKSUMS-SHA256.txt index e93e399..4df94de 100644 --- a/CHECKSUMS-SHA256.txt +++ b/CHECKSUMS-SHA256.txt @@ -1,2 +1,2 @@ -c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb writer/published/linux-x64/SkyrimForge.Native -b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc writer/published/win-x64/SkyrimForge.Native.exe +3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73 writer/published/linux-x64/SkyrimForge.Native +a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842 writer/published/win-x64/SkyrimForge.Native.exe diff --git a/CURRENT.txt b/CURRENT.txt index 26d99a2..09b254e 100644 --- a/CURRENT.txt +++ b/CURRENT.txt @@ -1 +1 @@ -5.2.1 +6.0.0 diff --git a/Install-or-Update.ps1 b/Install-or-Update.ps1 index 970dc04..345d248 100644 --- a/Install-or-Update.ps1 +++ b/Install-or-Update.ps1 @@ -1,4 +1,4 @@ -[CmdletBinding()] +[CmdletBinding()] param( [switch]$BootstrapPython ) @@ -11,8 +11,18 @@ $Utf8NoBom = New-Object System.Text.UTF8Encoding($false) function Invoke-Checked { param([string]$Label, [string]$Command, [object[]]$Arguments = @()) - & $Command @Arguments - if ($LASTEXITCODE -ne 0) { throw "$Label failed with exit code $LASTEXITCODE." } + Write-Host (" .. " + $Label + '...') -ForegroundColor DarkCyan + $Timer = [Diagnostics.Stopwatch]::StartNew() + $prevEap=$ErrorActionPreference; $ErrorActionPreference='Continue' + $Output = (& $Command @Arguments 2>&1 | Out-String) + $ErrorActionPreference=$prevEap + $ExitCode = $LASTEXITCODE + $Timer.Stop() + $Elapsed = $Timer.Elapsed.TotalSeconds + if ($ExitCode -ne 0) { + throw ("{0} failed with exit code {1} after {2:N1}s.`n{3}" -f $Label, $ExitCode, $Elapsed, $Output.Trim()) + } + Write-Host (" OK {0} ({1:N1}s)" -f $Label, $Elapsed) -ForegroundColor Green } function Find-Python { diff --git a/MANIFEST.json b/MANIFEST.json index 5b8e790..bdecc40 100644 --- a/MANIFEST.json +++ b/MANIFEST.json @@ -63,14 +63,14 @@ { "executable": false, "path": "CHANGELOG.md", - "sha256": "1fecd65ecbf38bf5d689df800948a71237eb0358913545b7a18e394a1e18d23e", - "size": 27267 + "sha256": "813bebbe733d1be02aef8ce4aa3c88ea406541c8264de3b79b8548b82d2dc1b1", + "size": 28803 }, { "executable": false, "path": "CHANGELOG.txt", - "sha256": "42499c7c42f18be0ef47cd7b088f996873a623b8e8b64eebfcf6513cc13216f1", - "size": 7350 + "sha256": "56ebf74e51a612a6dc0eb3d79e10dc227ed81f384765aefe37266a0f260727f3", + "size": 7848 }, { "executable": false, @@ -111,7 +111,7 @@ { "executable": false, "path": "CURRENT.txt", - "sha256": "4e001f5f34a8940927b497e5657a1bba9a35094b2c1f992de2fbee40af237d5b", + "sha256": "54ef76332b31df830699e9d4138396885ab72bae260a034a81ff02bf32d8e90a", "size": 6 }, { @@ -417,8 +417,8 @@ { "executable": false, "path": "Install-or-Update.ps1", - "sha256": "3f9b15bb9eac899b61213502d8ef9397b9e7d095473443ee7819b1f746b69bbb", - "size": 12866 + "sha256": "a04d7b1f751c3e02d966d533005978ec7407940d59dc584a4eb2fe1d0d507d43", + "size": 13370 }, { "executable": false, @@ -429,8 +429,8 @@ { "executable": false, "path": "integrations/skyrim-forge/SKILL.md", - "sha256": "7056e3f07e7afb034f7b8f4256bff8bee3173267ee36c702f2bf927b29714278", - "size": 6924 + "sha256": "c6a68a83a82e93706cf68fb058636a2500db547bebf5746a0aeee11d754ed3ff", + "size": 9012 }, { "executable": false, @@ -465,14 +465,14 @@ { "executable": false, "path": "pyproject.toml", - "sha256": "647433c2acb293942389d4a608251d2434dd9421c0204d828875d2b19db56d30", + "sha256": "9eb166626d01b53053dfac12d6037b0c8c67f558b5c457cec6bb005f955bb798", "size": 856 }, { "executable": false, "path": "README.md", - "sha256": "5b5bfef31613e78e383d9f2155b6933d0902f0bb5c6997cf47d36d9c7c9b203d", - "size": 15352 + "sha256": "dcce5677321c95228ae18bd6cf18e1478b1be0c7fd4223cad22bc5cd7697bd1e", + "size": 15623 }, { "executable": false, @@ -531,8 +531,8 @@ { "executable": false, "path": "Register-MCP.ps1", - "sha256": "13c500eb123f9e1ebd5690825c63965ef8418caa86c5d8c812e492aeca519bc2", - "size": 17151 + "sha256": "94f3aec362c767b68a5c33440cb87eb173d760a5502ff7be5174fccf1d0dec0f", + "size": 22856 }, { "executable": false, @@ -615,14 +615,14 @@ { "executable": false, "path": "scripts/rebuild_native_helpers.py", - "sha256": "9a2b377c73dcc09d2f0a463a3a3d90d4ea94e3e766c7b1271972a55d0db73222", - "size": 2994 + "sha256": "68e06fa6c05ffcdccacffa37fc6d1769c944a21cbd327157447916235e8126ac", + "size": 3248 }, { "executable": false, "path": "scripts/validate_repository.py", - "sha256": "a82ba940f57029f6935a4fba933a943ff938310cae38e70bdbc0ff7969bce05d", - "size": 37553 + "sha256": "8ea0ebf1b369bf78427d50ff656b4f5f25171977a4594dcf8cf1df08c513c680", + "size": 39128 }, { "executable": false, @@ -687,21 +687,15 @@ { "executable": false, "path": "skyrim_forge/bin/linux-x64/SkyrimForge.Native", - "sha256": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "sha256": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "size": 1839239 }, { "executable": true, "path": "skyrim_forge/bin/win-x64/SkyrimForge.Native.exe", - "sha256": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "sha256": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "size": 1955328 }, - { - "executable": false, - "path": "skyrim_forge/bundle_contract.py", - "sha256": "bf3264da46a640068bd36d944228b6967e7689ae7adaaf423cebc963dfcd1c1e", - "size": 1891 - }, { "executable": false, "path": "skyrim_forge/capabilities.py", @@ -711,8 +705,8 @@ { "executable": false, "path": "skyrim_forge/cli.py", - "sha256": "f13a983af3020ea93a66f47f750f4778cffaf992c3d4a96691c1919a253aef35", - "size": 14396 + "sha256": "ca5d0c73076c54798466364453bed580f7626582aeac9fd03fb77a0c7ac1851e", + "size": 14074 }, { "executable": false, @@ -1107,7 +1101,7 @@ { "executable": false, "path": "skyrim_forge/version.py", - "sha256": "d31d8b27665bf56756296461b0068afaf0ccd4d33daf71c8494b2ee00d8c4a75", + "sha256": "d4773dd6e1e1bf71fe85fb4adf2764f26afd8a29d60349549e257bd8e71d945e", "size": 18 }, { @@ -1131,8 +1125,8 @@ { "executable": true, "path": "START-HERE.bat", - "sha256": "8b06e21eda12c5503075e1148c382354ad7508deaccf8b0ea6a44500c35538cc", - "size": 2288 + "sha256": "8bc923a6bc5c004637dd37f9625127d998a5476088e1ff2222da553d24470e24", + "size": 4735 }, { "executable": false, @@ -1142,21 +1136,21 @@ }, { "executable": false, - "path": "tests/test_automation.py", - "sha256": "7c9ea76103e9fa8ad42f85f790dd394472af74fd2921837aaa11dbc31a0398e5", - "size": 8570 + "path": "tests/_workflows.py", + "sha256": "d8a6e9615587653e393165cb4167579f67d011ba65ae443d79938b479a68dedc", + "size": 1002 }, { "executable": false, - "path": "tests/test_bundle_contract_v520.py", - "sha256": "1ea2ef94f6ff99a2b810bf100bcedeaf7aff6887566357b46d9f2a0ae7e756f6", - "size": 2429 + "path": "tests/test_automation.py", + "sha256": "7c9ea76103e9fa8ad42f85f790dd394472af74fd2921837aaa11dbc31a0398e5", + "size": 8570 }, { "executable": false, "path": "tests/test_ci_validation.py", - "sha256": "7472597a6dfee2a49710c9fe3e83f2626c79e4dbf86326d32b90ec63559fe9cb", - "size": 3029 + "sha256": "2a52432ca8a4f4ae8435e790af72d710bb07e080928ffb9263e3f655a078509d", + "size": 4375 }, { "executable": false, @@ -1209,8 +1203,8 @@ { "executable": false, "path": "tests/test_powershell_release.py", - "sha256": "f24b86625e2966df0344dcc49cbc26fc6c50905e68ee648e6983b9ba220fb9a8", - "size": 7718 + "sha256": "264d8b2ebaf155f178e0cbca213dfe86ea6433d33ab2db45b255d75be24f5ab0", + "size": 8758 }, { "executable": false, @@ -1239,8 +1233,8 @@ { "executable": false, "path": "tests/test_windows_provider_bridge.py", - "sha256": "d98b8474aa56c328c0d460e899d3640cd8436dee754acfc115919f97ac9727ed", - "size": 10059 + "sha256": "c601c4accf46c8706b248cc291f26d17534f3ee8216c28dc1cc56d22baae8eff", + "size": 7885 }, { "executable": false, @@ -1251,7 +1245,7 @@ { "executable": false, "path": "VERSION.txt", - "sha256": "e5fd09eefef974eb2700ca4eaf41bbe6ba65cb75944dd2a05334db6bae3c1832", + "sha256": "73c1dcc6342beea4431cb0296846c8ca7bc0e573afd17e7825c6e74e5ebd6dd6", "size": 72 }, { @@ -1281,7 +1275,7 @@ { "executable": false, "path": "writer/native-go/main.go", - "sha256": "61c7ba376ade0fd9dd741f90862d71de2111628d37b9b85ec09d5e05c9bdf83d", + "sha256": "95fe7d4659b0ca17d40426fb2d0ff44492b16059996b86d8644d1c0594a12309", "size": 3641 }, { @@ -1293,16 +1287,16 @@ { "executable": false, "path": "writer/published/linux-x64/SkyrimForge.Native", - "sha256": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "sha256": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "size": 1839239 }, { "executable": true, "path": "writer/published/win-x64/SkyrimForge.Native.exe", - "sha256": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "sha256": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "size": 1955328 } ], "product": "Skyrim Forge", - "version": "5.2.1" + "version": "6.0.0" } diff --git a/README.md b/README.md index 70a1f28..0eb2f94 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,38 @@ -# Skyrim Forge 5.2.1 +# Skyrim Forge 6.0.0 -> ## Development has moved +> ## Development happens in the Ultimate AI Starter Bundle > -> Skyrim Forge is now developed inside the **Ultimate AI Starter Bundle**, at +> Skyrim Forge is developed inside the **Ultimate AI Starter Bundle**, at > [`BUNDLED-TOOLS/skyrim-forge`](https://github.com/ShugokiFable/Ultimate-AI-Starter-Bundle/tree/main/BUNDLED-TOOLS/skyrim-forge). -> This repository is frozen at **5.2.1**, its last standalone release. +> **This repository mirrors that source.** The code here is the code that ships; +> it is not a fork and not a separate line of development. > -> **Why.** The bundle installs Forge, and the two lived in separate +> **Why the move.** The bundle installs Forge, and the two lived in separate > repositories. Bundle 7.8.0 shipped an installer that read a field Forge has > never emitted, so the Forge step threw on every run and aborted the whole > install — with every gate green in both repositories, because no commit could > test both halves. Two files that must agree do not belong in two repositories > on two release schedules. Merged, one commit proves both. > -> **Where Forge is now.** Forge **6.0.0** and later ship inside the bundle: +> **Where changes land first.** In the bundle. This mirror is updated when a +> Forge version is cut, not per-commit, so it can trail by a release. For the +> newest Forge, and for everything else the pack installs, use the > [Ultimate-AI-Starter-Bundle releases](https://github.com/ShugokiFable/Ultimate-AI-Starter-Bundle/releases). -> Run `START-HERE.bat`, or install Forge alone with -> `TOOLS\Install-SkyrimForge.ps1 -ForgeRoot 'S:\Apps\Skyrim Tools\Skyrim-Forge'`. -> Its source is a normal directory in that repository — read it, edit it, build -> it, with no submodule and no download step. > -> **If you are on 5.2.1.** It keeps working; nothing here was withdrawn, and the +> **Issues and pull requests belong in the bundle** — +> [Ultimate-AI-Starter-Bundle/issues](https://github.com/ShugokiFable/Ultimate-AI-Starter-Bundle/issues). +> A fix made here alone would be overwritten by the next mirror update; a fix +> made there reaches both. +> +> **Coming from 5.2.1?** It keeps working and the > [v5.2.1 release](https://github.com/ShugokiFable/SkyrimForge/releases/tag/v5.2.1) -> stays downloadable. Upgrading is a normal install: the bundle's installer -> migrates a version-stamped `Skyrim-Forge-5.2.1` folder onto the versionless +> stays downloadable. Upgrading is a normal install: the installer migrates a +> version-stamped `Skyrim-Forge-5.2.1` folder onto the versionless > `Skyrim-Forge` name and preserves `Workspaces`, `.venv`, `REPORTS` and a local > `config.toml`. 6.0.0 removes the `forge bundle-contract` command, which existed -> only to negotiate across the repository split this notice is about. -> -> **Support for this repository has been dropped.** 5.2.1 is not maintained: -> no fixes, no releases, no patch versions. Issues and pull requests opened -> here will be closed with a pointer rather than worked on. Report anything -> against Forge in the bundle instead — -> [Ultimate-AI-Starter-Bundle/issues](https://github.com/ShugokiFable/Ultimate-AI-Starter-Bundle/issues). -> The code stays public and the release stays downloadable; only the -> maintenance stops. -> -> Everything below describes 5.2.1 and is kept as-is for that release. +> only to negotiate across the repository split this notice is about — one +> repository cannot usefully negotiate a version range with itself, and a stale +> range can only start rejecting the pack it ships inside. Skyrim Forge is a local, safety-first engineering workbench and MCP server for Skyrim Special Edition and Anniversary Edition mod development. @@ -46,7 +42,7 @@ handshake era every installed AI client uses today. Both eras run from the same process and expose the same 52 tools, so existing Codex, Claude, and Grok registrations need no change. See [docs/MCP.md](docs/MCP.md). -Forge 4.2 includes the **Automation Fabric** plus a mandatory rights-and-publication gate for shareable releases. An AI does not click around SSEEdit, Creation Kit, Wrye Bash, LOOT, or Mod Organizer 2. It submits a typed JSON job. Forge validates the job, snapshots inputs, runs only the configured adapter, captures logs and outputs, reopens what it can verify, and writes an audit receipt. +Forge includes the **Automation Fabric** plus a mandatory rights-and-publication gate for shareable releases. An AI does not click around SSEEdit, Creation Kit, Wrye Bash, LOOT, or Mod Organizer 2. It submits a typed JSON job. Forge validates the job, snapshots inputs, runs only the configured adapter, captures logs and outputs, reopens what it can verify, and writes an audit receipt. ## Public and Nexus releases @@ -62,17 +58,25 @@ The gate maps every bundled file to its origin, author, licence or permission ba ## Install +Most people should install the +[Ultimate AI Starter Bundle](https://github.com/ShugokiFable/Ultimate-AI-Starter-Bundle) +and run its `START-HERE.bat`, which installs Forge along with everything else +the pack wires up. To install Forge on its own from this repository: + 1. Extract the release into your **Skyrim tools folder** as `Skyrim-Forge` (same directory as xEdit, houseCARL, Spooky, …). Leave the version out of the folder name: every AI provider stores the - MCP command as an absolute path, so a `Skyrim-Forge-5.2.1` folder + MCP command as an absolute path, so a `Skyrim-Forge-6.0.0` folder silently disconnects all of them the next time you upgrade. The installed version is in `VERSION.txt`. Do not clone or extract into `Documents`. Do not keep a second copy. -2. Run `START-HERE.bat`. -3. Choose **Install/update Forge and connect all detected AI apps**. -4. Configure core paths. Job staging defaults to `\Workspaces`. -5. Configure only the external tools actually installed on the machine. +2. Run `START-HERE.bat`. With no arguments it installs or updates Forge and + connects every detected AI app, so re-running it is also how you upgrade. +3. Configure core paths. Job staging defaults to `\Workspaces`. +4. Configure only the external tools actually installed on the machine. + +The other ten maintenance actions are one keypress away: `START-HERE.bat --menu`, +or press **M** when the install finishes. Claude Code 2026-07-28 requires Forge **5.1.5 or newer** (`tools/call` must carry `resultType`). Grok wedges at 8 running MCP servers; `Register-MCP.ps1` diff --git a/Register-MCP.ps1 b/Register-MCP.ps1 index c47ccbc..971a71e 100644 --- a/Register-MCP.ps1 +++ b/Register-MCP.ps1 @@ -1,4 +1,4 @@ -[CmdletBinding()] +[CmdletBinding()] param( [ValidateSet('All', 'Codex', 'Claude', 'Grok', 'Kimi', 'Hermes')] [string]$Provider = 'All', @@ -133,7 +133,9 @@ function Invoke-Registration { try { switch ($Name) { 'Codex' { - & $Executable mcp remove skyrim-forge 2>$null | Out-Null + $ErrorActionPreference = 'Continue' + & $Executable mcp remove skyrim-forge 2>&1 | Out-Null + $ErrorActionPreference = 'Stop' & $Executable mcp add skyrim-forge -- $Python -m skyrim_forge mcp | Out-Null if ($LASTEXITCODE -ne 0) { throw "Codex registration exited $LASTEXITCODE." } & $Executable mcp get skyrim-forge | Out-Null @@ -142,7 +144,9 @@ function Invoke-Registration { 'Claude' { if ($Executable) { # Claude Code CLI surface (~/.claude.json) - & $Executable mcp remove skyrim-forge -s user 2>$null | Out-Null + $ErrorActionPreference = 'Continue' + & $Executable mcp remove skyrim-forge -s user 2>&1 | Out-Null + $ErrorActionPreference = 'Stop' & $Executable mcp add --transport stdio --scope user skyrim-forge -- $Python -m skyrim_forge mcp | Out-Null if ($LASTEXITCODE -ne 0) { throw "Claude registration exited $LASTEXITCODE." } } @@ -161,7 +165,9 @@ function Invoke-Registration { detail = 'Grok wedges at 8 running MCP servers. Forge was not added. Run grok mcp disable mcp-search and/or disable another server, then rerun Register-MCP.ps1 -Provider Grok. Require Forge 5.1.5+ so tools/call carries resultType.' } } - & $Executable mcp remove skyrim-forge 2>$null | Out-Null + $ErrorActionPreference = 'Continue' + & $Executable mcp remove skyrim-forge 2>&1 | Out-Null + $ErrorActionPreference = 'Stop' # Windows PowerShell 5 rewrites native `--` boundaries when the # executable is invoked through a variable. Start-Process keeps # Grok's documented separator and Python's `-m` as server args. @@ -234,14 +240,93 @@ function Invoke-Registration { $HermesHome = if ($env:HERMES_HOME) { $env:HERMES_HOME } else { Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'hermes' } $HermesConfigPath = Join-Path $HermesHome 'config.yaml' $HermesOriginal = if (Test-Path -LiteralPath $HermesConfigPath -PathType Leaf) { [IO.File]::ReadAllBytes($HermesConfigPath) } else { $null } + $HermesPython = Join-Path (Split-Path -Parent $Executable) 'python.exe' + if (-not (Test-Path -LiteralPath $HermesPython -PathType Leaf)) { throw "Hermes Python runtime missing beside executable: $HermesPython" } + $HermesConfigHelper = Join-Path ([IO.Path]::GetTempPath()) ('uabs-hermes-forge-' + [guid]::NewGuid().ToString('N') + '.py') + $HermesStdout = Join-Path ([IO.Path]::GetTempPath()) ('uabs-hermes-probe-' + [guid]::NewGuid().ToString('N') + '.out') + $HermesStderr = $HermesStdout + '.err' + $HermesResult = $HermesStdout + '.json' + $OldForgePython = $env:UABS_FORGE_PYTHON + $OldHermesResult = $env:UABS_HERMES_RESULT try { - & $Executable mcp remove skyrim-forge 2>$null | Out-Null - 'Y' | & $Executable mcp add skyrim-forge --command $Python --args -m skyrim_forge mcp | Out-Null - if ($LASTEXITCODE -ne 0) { throw "Hermes registration exited $LASTEXITCODE." } - $HermesTestOutput = (& $Executable mcp test skyrim-forge 2>&1 | Out-String) - if ($LASTEXITCODE -ne 0 -or $HermesTestOutput -notmatch '(?im)(connected|found\s+\d+\s+tool)') { - throw "Hermes MCP test did not confirm a Forge connection: $HermesTestOutput" + # Do NOT launch hermes.exe here. Full CLI startup registers shell hooks, + # and Hermes intentionally prompts on first use of unseen hooks when stdin + # is a TTY. That made an unattended bundle install wait for Enter. Use the + # same low-level MCP probe that `hermes mcp test` and Hermes doctor call, + # without starting the interactive CLI layer at all. + $HelperSource = @' +import json +import os +from pathlib import Path +from hermes_cli.config import load_config, save_config +from hermes_cli.mcp_config import _probe_single_server + +result_path = Path(os.environ["UABS_HERMES_RESULT"]) +server = { + "command": os.environ["UABS_FORGE_PYTHON"], + "args": ["-m", "skyrim_forge", "mcp"], + "enabled": True, + "connect_timeout": 30, +} +try: + cfg = load_config() + servers = cfg.setdefault("mcp_servers", {}) + servers["skyrim-forge"] = server + save_config(cfg) + + verify = load_config().get("mcp_servers", {}).get("skyrim-forge", {}) + if verify.get("command") != server["command"] or verify.get("args") != server["args"]: + raise RuntimeError("saved Hermes Forge MCP entry did not round-trip exactly") + + tools = _probe_single_server("skyrim-forge", verify, connect_timeout=30) + result_path.write_text(json.dumps({ + "connected": True, + "tool_count": len(tools), + "tools": [item[0] for item in tools], + }), encoding="utf-8") +except Exception as exc: + result_path.write_text(json.dumps({ + "connected": False, + "error": f"{type(exc).__name__}: {exc}", + }), encoding="utf-8") + raise +'@ + $Utf8NoBom = New-Object System.Text.UTF8Encoding($false) + [IO.File]::WriteAllText($HermesConfigHelper, $HelperSource, $Utf8NoBom) + $env:UABS_FORGE_PYTHON = $Python + $env:UABS_HERMES_RESULT = $HermesResult + Write-Host ' .. Hermes: saving + probing Forge MCP directly (hard limit 45s; no CLI prompts)...' -ForegroundColor DarkCyan + $HermesProcess = Start-Process -FilePath $HermesPython -ArgumentList @($HermesConfigHelper) -NoNewWindow -PassThru -RedirectStandardOutput $HermesStdout -RedirectStandardError $HermesStderr + $HermesStarted = [DateTime]::UtcNow + $HermesNextProgress = 5 + $HermesExited = $HermesProcess.WaitForExit(250) + while (-not $HermesExited) { + $Elapsed = [int]([DateTime]::UtcNow - $HermesStarted).TotalSeconds + if ($Elapsed -ge 45) { + try { $HermesProcess.Kill() } catch {} + try { $HermesProcess.WaitForExit() } catch {} + throw 'Hermes direct MCP probe timed out after 45 seconds.' + } + if ($Elapsed -ge $HermesNextProgress) { + Write-Host (" .. Hermes: direct Forge MCP probe still running ($Elapsed s / 45 s)...") -ForegroundColor DarkCyan + $HermesNextProgress += 5 + } + $HermesExited = $HermesProcess.WaitForExit(250) } + # On Windows PowerShell 5.1/.NET Framework, a timed WaitForExit() can + # report completion before redirected streams and ExitCode are fully + # finalized. The parameterless call is required before consuming either. + $HermesProcess.WaitForExit() + $HermesProcess.Refresh() + $HermesExitCode = [int]$HermesProcess.ExitCode + $HermesOutput = '' + if (Test-Path -LiteralPath $HermesStdout -PathType Leaf) { $HermesOutput += [IO.File]::ReadAllText($HermesStdout) } + if (Test-Path -LiteralPath $HermesStderr -PathType Leaf) { $HermesOutput += [IO.File]::ReadAllText($HermesStderr) } + if ($HermesExitCode -ne 0) { throw "Hermes direct MCP probe exited ${HermesExitCode}: $HermesOutput" } + if (-not (Test-Path -LiteralPath $HermesResult -PathType Leaf)) { throw "Hermes direct MCP probe produced no result file: $HermesOutput" } + $HermesProbe = [IO.File]::ReadAllText($HermesResult) | ConvertFrom-Json + if (-not $HermesProbe.connected) { throw "Hermes direct MCP probe did not connect: $($HermesProbe.error)" } + Write-Host (" OK Hermes: Forge MCP connection verified ({0} tool(s))" -f $HermesProbe.tool_count) -ForegroundColor Green } catch { if ($null -ne $HermesOriginal) { [IO.File]::WriteAllBytes($HermesConfigPath, $HermesOriginal) @@ -249,6 +334,10 @@ function Invoke-Registration { Remove-Item -LiteralPath $HermesConfigPath -Force } throw + } finally { + if ($null -eq $OldForgePython) { Remove-Item Env:UABS_FORGE_PYTHON -ErrorAction SilentlyContinue } else { $env:UABS_FORGE_PYTHON = $OldForgePython } + if ($null -eq $OldHermesResult) { Remove-Item Env:UABS_HERMES_RESULT -ErrorAction SilentlyContinue } else { $env:UABS_HERMES_RESULT = $OldHermesResult } + Remove-Item -LiteralPath $HermesConfigHelper,$HermesStdout,$HermesStderr,$HermesResult -Force -ErrorAction SilentlyContinue } } } diff --git a/SBOM.spdx.json b/SBOM.spdx.json index 2ab5309..c6851b7 100644 --- a/SBOM.spdx.json +++ b/SBOM.spdx.json @@ -3,11 +3,11 @@ "creationInfo": { "created": "2026-07-24T00:00:00Z", "creators": [ - "Tool: Skyrim Forge validator 5.2.1" + "Tool: Skyrim Forge validator 6.0.0" ] }, "dataLicense": "CC0-1.0", - "documentNamespace": "https://example.invalid/skyrim-forge/5.2.1/spdx", + "documentNamespace": "https://example.invalid/skyrim-forge/6.0.0/spdx", "files": [ { "SPDXID": "SPDXRef-File-.editorconfig", @@ -119,7 +119,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "e583f92372898c3e3f3b74c11acde91fed384dc61e50ed7bd72c73b4e4bb3b2c" + "checksumValue": "9bb18ddea813430d8c0bc134f33ea12b9632eee23a761e0715b4a817069c171f" } ], "copyrightText": "NOASSERTION", @@ -164,7 +164,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "1fecd65ecbf38bf5d689df800948a71237eb0358913545b7a18e394a1e18d23e" + "checksumValue": "813bebbe733d1be02aef8ce4aa3c88ea406541c8264de3b79b8548b82d2dc1b1" } ], "copyrightText": "NOASSERTION", @@ -179,7 +179,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "42499c7c42f18be0ef47cd7b088f996873a623b8e8b64eebfcf6513cc13216f1" + "checksumValue": "56ebf74e51a612a6dc0eb3d79e10dc227ed81f384765aefe37266a0f260727f3" } ], "copyrightText": "NOASSERTION", @@ -284,7 +284,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "4e001f5f34a8940927b497e5657a1bba9a35094b2c1f992de2fbee40af237d5b" + "checksumValue": "54ef76332b31df830699e9d4138396885ab72bae260a034a81ff02bf32d8e90a" } ], "copyrightText": "NOASSERTION", @@ -1049,7 +1049,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "3f9b15bb9eac899b61213502d8ef9397b9e7d095473443ee7819b1f746b69bbb" + "checksumValue": "a04d7b1f751c3e02d966d533005978ec7407940d59dc584a4eb2fe1d0d507d43" } ], "copyrightText": "NOASSERTION", @@ -1079,7 +1079,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "7056e3f07e7afb034f7b8f4256bff8bee3173267ee36c702f2bf927b29714278" + "checksumValue": "c6a68a83a82e93706cf68fb058636a2500db547bebf5746a0aeee11d754ed3ff" } ], "copyrightText": "NOASSERTION", @@ -1169,7 +1169,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "647433c2acb293942389d4a608251d2434dd9421c0204d828875d2b19db56d30" + "checksumValue": "9eb166626d01b53053dfac12d6037b0c8c67f558b5c457cec6bb005f955bb798" } ], "copyrightText": "NOASSERTION", @@ -1184,7 +1184,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "6515572a5602097dbf6d42efa9c3f8b8c38aac124395a1b7f798118e3c9eabb4" + "checksumValue": "dcce5677321c95228ae18bd6cf18e1478b1be0c7fd4223cad22bc5cd7697bd1e" } ], "copyrightText": "NOASSERTION", @@ -1334,7 +1334,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "13c500eb123f9e1ebd5690825c63965ef8418caa86c5d8c812e492aeca519bc2" + "checksumValue": "94f3aec362c767b68a5c33440cb87eb173d760a5502ff7be5174fccf1d0dec0f" } ], "copyrightText": "NOASSERTION", @@ -1544,7 +1544,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "9a2b377c73dcc09d2f0a463a3a3d90d4ea94e3e766c7b1271972a55d0db73222" + "checksumValue": "68e06fa6c05ffcdccacffa37fc6d1769c944a21cbd327157447916235e8126ac" } ], "copyrightText": "NOASSERTION", @@ -1559,7 +1559,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "b3e1614b8132ad595b90f7cb85caec8aeab57abe0c7bda35eb3b47cb6dc9cb1a" + "checksumValue": "8ea0ebf1b369bf78427d50ff656b4f5f25171977a4594dcf8cf1df08c513c680" } ], "copyrightText": "NOASSERTION", @@ -1724,7 +1724,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb" + "checksumValue": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73" } ], "copyrightText": "NOASSERTION", @@ -1739,7 +1739,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "checksumValue": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" } ], "copyrightText": "NOASSERTION", @@ -1749,21 +1749,6 @@ "NOASSERTION" ] }, - { - "SPDXID": "SPDXRef-File-skyrim-forge-bundle-contract.py", - "checksums": [ - { - "algorithm": "SHA256", - "checksumValue": "bf3264da46a640068bd36d944228b6967e7689ae7adaaf423cebc963dfcd1c1e" - } - ], - "copyrightText": "NOASSERTION", - "fileName": "./skyrim_forge/bundle_contract.py", - "licenseConcluded": "NOASSERTION", - "licenseInfoInFiles": [ - "NOASSERTION" - ] - }, { "SPDXID": "SPDXRef-File-skyrim-forge-capabilities.py", "checksums": [ @@ -1784,7 +1769,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "f13a983af3020ea93a66f47f750f4778cffaf992c3d4a96691c1919a253aef35" + "checksumValue": "ca5d0c73076c54798466364453bed580f7626582aeac9fd03fb77a0c7ac1851e" } ], "copyrightText": "NOASSERTION", @@ -2774,7 +2759,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "d31d8b27665bf56756296461b0068afaf0ccd4d33daf71c8494b2ee00d8c4a75" + "checksumValue": "d4773dd6e1e1bf71fe85fb4adf2764f26afd8a29d60349549e257bd8e71d945e" } ], "copyrightText": "NOASSERTION", @@ -2834,7 +2819,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "8b06e21eda12c5503075e1148c382354ad7508deaccf8b0ea6a44500c35538cc" + "checksumValue": "8bc923a6bc5c004637dd37f9625127d998a5476088e1ff2222da553d24470e24" } ], "copyrightText": "NOASSERTION", @@ -2860,30 +2845,30 @@ ] }, { - "SPDXID": "SPDXRef-File-tests-test-automation.py", + "SPDXID": "SPDXRef-File-tests--workflows.py", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "7c9ea76103e9fa8ad42f85f790dd394472af74fd2921837aaa11dbc31a0398e5" + "checksumValue": "d8a6e9615587653e393165cb4167579f67d011ba65ae443d79938b479a68dedc" } ], "copyrightText": "NOASSERTION", - "fileName": "./tests/test_automation.py", + "fileName": "./tests/_workflows.py", "licenseConcluded": "NOASSERTION", "licenseInfoInFiles": [ "NOASSERTION" ] }, { - "SPDXID": "SPDXRef-File-tests-test-bundle-contract-v520.py", + "SPDXID": "SPDXRef-File-tests-test-automation.py", "checksums": [ { "algorithm": "SHA256", - "checksumValue": "1ea2ef94f6ff99a2b810bf100bcedeaf7aff6887566357b46d9f2a0ae7e756f6" + "checksumValue": "7c9ea76103e9fa8ad42f85f790dd394472af74fd2921837aaa11dbc31a0398e5" } ], "copyrightText": "NOASSERTION", - "fileName": "./tests/test_bundle_contract_v520.py", + "fileName": "./tests/test_automation.py", "licenseConcluded": "NOASSERTION", "licenseInfoInFiles": [ "NOASSERTION" @@ -2894,7 +2879,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "7472597a6dfee2a49710c9fe3e83f2626c79e4dbf86326d32b90ec63559fe9cb" + "checksumValue": "2a52432ca8a4f4ae8435e790af72d710bb07e080928ffb9263e3f655a078509d" } ], "copyrightText": "NOASSERTION", @@ -3029,7 +3014,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "d5d4d3e57f63b5787f1aa5a12bb36a875a76c6f765fc0d6f8077f057a4c9a4ad" + "checksumValue": "264d8b2ebaf155f178e0cbca213dfe86ea6433d33ab2db45b255d75be24f5ab0" } ], "copyrightText": "NOASSERTION", @@ -3104,7 +3089,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "d98b8474aa56c328c0d460e899d3640cd8436dee754acfc115919f97ac9727ed" + "checksumValue": "c601c4accf46c8706b248cc291f26d17534f3ee8216c28dc1cc56d22baae8eff" } ], "copyrightText": "NOASSERTION", @@ -3134,7 +3119,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "e5fd09eefef974eb2700ca4eaf41bbe6ba65cb75944dd2a05334db6bae3c1832" + "checksumValue": "73c1dcc6342beea4431cb0296846c8ca7bc0e573afd17e7825c6e74e5ebd6dd6" } ], "copyrightText": "NOASSERTION", @@ -3209,7 +3194,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "61c7ba376ade0fd9dd741f90862d71de2111628d37b9b85ec09d5e05c9bdf83d" + "checksumValue": "95fe7d4659b0ca17d40426fb2d0ff44492b16059996b86d8644d1c0594a12309" } ], "copyrightText": "NOASSERTION", @@ -3239,7 +3224,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb" + "checksumValue": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73" } ], "copyrightText": "NOASSERTION", @@ -3254,7 +3239,7 @@ "checksums": [ { "algorithm": "SHA256", - "checksumValue": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "checksumValue": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" } ], "copyrightText": "NOASSERTION", @@ -3265,7 +3250,7 @@ ] } ], - "name": "Skyrim-Forge-5.2.1", + "name": "Skyrim-Forge-6.0.0", "packages": [ { "SPDXID": "SPDXRef-Package", @@ -3275,7 +3260,7 @@ "licenseConcluded": "MIT", "licenseDeclared": "MIT", "name": "Skyrim Forge", - "versionInfo": "5.2.1" + "versionInfo": "6.0.0" } ], "spdxVersion": "SPDX-2.3" diff --git a/START-HERE.bat b/START-HERE.bat index 8b054c6..458b9d5 100644 --- a/START-HERE.bat +++ b/START-HERE.bat @@ -1,6 +1,6 @@ @echo off setlocal EnableExtensions -title Skyrim Forge 5.2.1 +title Skyrim Forge 6.0.0 set "FORGE_PS_GATE=%~dp0PowerShell-Parse-Gate.ps1" powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%FORGE_PS_GATE%" if errorlevel 1 ( @@ -10,11 +10,67 @@ if errorlevel 1 ( exit /b 1 ) if /I "%~1"=="--validate-only" exit /b 0 +if /I "%~1"=="--menu" goto menu +if not "%~1"=="" goto menu + +rem No arguments: do the thing almost everyone who opens this file came to do. +rem This landed on an eleven-item menu whose correct answer was "1" for anyone +rem who had not used Forge before -- and the bundle installer never shows this +rem menu at all, so the only person who ever saw it was a first-timer opening +rem the Forge folder directly. The other ten entries are one keypress away +rem (--menu, or M when this finishes). Install-AI-Bridge installs OR updates +rem and then reconnects every detected AI app, so re-running this is also how +rem you update. +goto autoinstall + +:autoinstall +cls +echo. +echo ================================================================ +echo SKYRIM FORGE 6.0.0 - VERIFIED TOOLCHAIN FABRIC +echo ================================================================ +echo. +echo Installing Forge and connecting every detected AI app. +echo This installs or updates, so it is safe to run again. +echo. +echo More options: START-HERE.bat --menu (or press M at the end) +echo ================================================================ +echo. +powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%~dp0Install-AI-Bridge.ps1" -BootstrapPython -Yes +set "BRIDGE_EXIT=%ERRORLEVEL%" +echo. +if not "%BRIDGE_EXIT%"=="0" ( + echo ================================================================ + echo SETUP FAILED - exit code %BRIDGE_EXIT% + echo ================================================================ + echo Read the error above. Two things worth trying: + echo * option 2 in the menu, to set your core paths + echo * option 4 in the menu, to run the full doctor + echo. + choice /C MX /N /M "Press M for the menu, X to exit: " + if errorlevel 2 exit /b %BRIDGE_EXIT% + goto menu +) +echo ================================================================ +echo READY - Forge is installed and every detected AI app is wired. +echo ================================================================ +echo. +echo Just talk to your AI app about your mod. It can drive Forge now. +echo. +echo Optional, only if you want them: +echo * option 2 set core paths (game, mods, output) +echo * option 3 point Forge at xEdit, MO2, CK, LOOT, Wrye, Papyrus +echo * option 8 open the Forge GUI +echo. +choice /C MX /N /M "Press M for more options, X to finish: " +if errorlevel 2 exit /b 0 +goto menu + :menu cls echo. echo ================================================================ -echo SKYRIM FORGE 5.2.1 - VERIFIED TOOLCHAIN FABRIC +echo SKYRIM FORGE 6.0.0 - VERIFIED TOOLCHAIN FABRIC echo ================================================================ echo. echo 1. Install/update Forge and connect all detected AI apps diff --git a/VALIDATION.json b/VALIDATION.json index dde2b89..012efb0 100644 --- a/VALIDATION.json +++ b/VALIDATION.json @@ -6,19 +6,19 @@ "json": 55, "pascal": 4, "powershell": 9, - "python": 65, + "python": 64, "toml": 2, "xml": 0, "yaml": 6 }, - "file_count": 217, + "file_count": 216, "third_party_binary_bundling": "disabled" }, "go": { "builds": { "linux": { "buildvcs": false, - "bundled": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "bundled": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "bundled_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", @@ -30,24 +30,24 @@ "stderr": "", "stdout": "\\writer\\published\\linux-x64\\SkyrimForge.Native: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" }, - "first": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", + "first": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", "first_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", "version", "-m", - "\\AppData\\Local\\Temp\\tmp6e19rlye\\linux-a" + "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\linux-a" ], "returncode": 0, "stderr": "", - "stdout": "\\AppData\\Local\\Temp\\tmp6e19rlye\\linux-a: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" + "stdout": "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\linux-a: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=linux\n\tbuild\tGOAMD64=v1\n" }, "result": "PASS", - "second": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb" + "second": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73" }, "windows": { "buildvcs": false, - "bundled": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "bundled": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "bundled_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", @@ -59,20 +59,20 @@ "stderr": "", "stdout": "\\writer\\published\\win-x64\\SkyrimForge.Native.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" }, - "first": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc", + "first": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842", "first_metadata": { "command": [ "C:\\Program Files\\Go\\bin\\go.EXE", "version", "-m", - "\\AppData\\Local\\Temp\\tmp6e19rlye\\windows-a.exe" + "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\windows-a.exe" ], "returncode": 0, "stderr": "", - "stdout": "\\AppData\\Local\\Temp\\tmp6e19rlye\\windows-a.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" + "stdout": "\\AppData\\Local\\Temp\\tmpbbb0pgpy\\windows-a.exe: go1.23.2\n\tpath\tskyrimforge/native\n\tmod\tskyrimforge/native\t(devel)\t\n\tbuild\t-buildmode=exe\n\tbuild\t-compiler=gc\n\tbuild\t-trimpath=true\n\tbuild\tCGO_ENABLED=0\n\tbuild\tGOARCH=amd64\n\tbuild\tGOOS=windows\n\tbuild\tGOAMD64=v1\n" }, "result": "PASS", - "second": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "second": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" } }, "format": { @@ -163,14 +163,14 @@ "native": { "executed_platform": "windows", "hashes": { - "linux": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", - "windows": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "linux": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", + "windows": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" }, "linux_elf": true, "package_copies_match": true, "package_copy_hashes": { - "linux": "c9bed3c6e78890f9c838058038902a3b75b8eda806563cc174e837118a5b1deb", - "windows": "b7164c899771a36d035da39f70ae50983edbb3cb88539cb38dab4e881a7019cc" + "linux": "3c573e52b15b926f3dd54418f4a48eeddc4e0b6c05d893e4471b87dcc53ffa73", + "windows": "a210e332016837167022f0900e1e2bbfca0bc46be2625c48ae0ab637f248d842" }, "result": "PASS", "self_test": { @@ -189,20 +189,20 @@ ], "returncode": 0, "stderr": "", - "stdout": "SkyrimForge.Native 5.2.1 go\n" + "stdout": "SkyrimForge.Native 6.0.0 go\n" }, "windows_pe": true }, "packaging": { "sdist": { "result": "PASS", - "sha256": "8d16c084c9277bedea2fdff2a802facbe39806deeea6ba1cea19cf3915552ef0", - "size": 3617128 + "sha256": "04025ffa166f528f19791391193e9e503395eb38053820e92b73b3e60ae3ed5d", + "size": 3621568 }, "wheel": { "result": "PASS", - "sha256": "e1a95db92bb7a791dfa3325b2f489e65d4ee2deeb28fcb3e55b9aefca9228235", - "size": 1848775 + "sha256": "fd074aa6126fd79c0a3ccf5c33833129c41017cb74351566393f02309f029789", + "size": 1848764 } }, "powershell": { @@ -231,7 +231,7 @@ "python": { "compile": { "command": [ - "\\.venv\\Scripts\\python.exe", + "\\AppData\\Local\\Python\\pythoncore-3.14-64\\python.exe", "-m", "compileall", "-q", @@ -246,19 +246,19 @@ "result": "PASS", "self_test": { "command": [ - "\\.venv\\Scripts\\python.exe", + "\\AppData\\Local\\Python\\pythoncore-3.14-64\\python.exe", "-m", "skyrim_forge", "self-test" ], "returncode": 0, "stderr": "", - "stdout": "{\n \"checks\": {\n \"capabilities\": {\n \"capabilities\": [\n {\n \"id\": \"plugin.inspect\",\n \"level\": \"direct\",\n \"limitations\": \"Forge's parser is independent of xEdit but is not a replacement for xEdit or runtime validation.\",\n \"outputs\": [\n \"headers\",\n \"masters\",\n \"record queries\",\n \"hashes\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"plugin.write.low_risk\",\n \"level\": \"direct\",\n \"limitations\": \"Typed records only; no arbitrary binary record editing.\",\n \"outputs\": [\n \"KYWD\",\n \"GLOB\",\n \"FLST\",\n \"OTFT\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"framework.lint\",\n \"level\": \"profiled\",\n \"limitations\": \"Unknown or version-specific syntax is reported as unverified, not automatically rewritten.\",\n \"profiles\": [\n \"SPID 7.3\",\n \"KID 4.0.6\",\n \"BOS 3.4.1\",\n \"SkyPatcher 6.4.2 placement/core rules\",\n \"FLM 1.8.1 core rules\",\n \"CDF pinned JSON subset\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"framework.build\",\n \"level\": \"profiled\",\n \"limitations\": \"Generates only modeled subsets and validates the generated file. It does not invent category-specific SkyPatcher properties.\",\n \"profiles\": [\n \"SPID 7.3\",\n \"KID 4.0.6\",\n \"BOS 3.4.1\",\n \"SkyPatcher 6.4.2 core INI\",\n \"FLM 1.8.1 core INI\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.analyze\",\n \"level\": \"profiled\",\n \"limitations\": \"Performance findings are review warnings, never automatic semantic rewrites.\",\n \"outputs\": [\n \"identity graph\",\n \"inheritance cycles\",\n \"import conflicts\",\n \"performance heuristics\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.compile\",\n \"level\": \"adapter\",\n \"limitations\": \"Compilation proves compiler acceptance and fresh PEX output, not runtime correctness.\",\n \"requires\": [\n \"pinned Bethesda PapyrusCompiler\",\n \"flags file\",\n \"source imports\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.scaffold\",\n \"level\": \"profiled\",\n \"limitations\": \"The scaffold is source-locked; installed compilers and dependencies remain external.\",\n \"outputs\": [\n \"CommonLibSSE-NG CMake project\",\n \"vcpkg manifests\",\n \"SKSE entry point\",\n \"CI workflow\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.build\",\n \"level\": \"adapter\",\n \"limitations\": \"Builds only inside the Forge workspace and does not deploy to live Data.\",\n \"requires\": [\n \"pinned CMake\",\n \"pinned vcpkg\",\n \"Visual Studio C++ toolchain\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.binary_audit\",\n \"level\": \"direct\",\n \"limitations\": \"Static PE evidence does not prove compatibility with every SKSE plugin or runtime.\",\n \"outputs\": [\n \"PE architecture\",\n \"DLL characteristic\",\n \"imports\",\n \"hash\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"fomod.engineering\",\n \"level\": \"profiled\",\n \"limitations\": \"Declarative ModuleConfig only; arbitrary C# installers are blocked.\",\n \"outputs\": [\n \"typed plan\",\n \"ModuleConfig XML\",\n \"simulation\",\n \"release gate\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"xedit.automation\",\n \"level\": \"adapter\",\n \"limitations\": \"The xEdit process may show a window, but jobs require completion markers and no user interaction.\",\n \"requires\": [\n \"installed hash-pinned xEdit\",\n \"allowlisted Pascal scripts\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"creation_kit\",\n \"level\": \"worker_contract\",\n \"limitations\": \"No universal headless CK bridge is bundled. UI automation is narrow and coordinate-free.\",\n \"requires\": [\n \"version-pinned local CK or CKPE worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"wrye_bash\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Requires a version-pinned worker around the installed Wrye Bash version.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"loot\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Forge validates and applies typed plans; it does not invent unsupported LOOT GUI arguments.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"mo2\",\n \"level\": \"adapter\",\n \"limitations\": \"Profile capture and bounded launching only; live profile changes require approval.\",\n \"status\": \"implemented\"\n },\n {\n \"id\": \"vortex\",\n \"level\": \"adapter\",\n \"limitations\": \"Forge inspects staging and builds archives. It does not mutate Vortex's internal database.\",\n \"status\": \"read_only\"\n },\n {\n \"id\": \"nexus.publication\",\n \"level\": \"human_gate\",\n \"limitations\": \"Forge enforces machine-checkable declarations and evidence completeness, but cannot authenticate legal ownership, consent, trademark/privacy clearance, or permission conversations. The uploader must attest and remains responsible.\",\n \"outputs\": [\n \"rights manifest\",\n \"credits\",\n \"third-party notices\",\n \"permissions\",\n \"AI disclosure\",\n \"private evidence audit\",\n \"Nexus BBCode page\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"runtime.skyrim\",\n \"level\": \"human_gate\",\n \"limitations\": \"Crashes, visuals, balance, navmesh, save migration, hooks, animation quality, and gameplay require real runtime testing.\",\n \"status\": \"required\"\n },\n {\n \"id\": \"synthesis\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Requires a pinned local Synthesis worker and patcher source; Forge can confine outputs and receipts but does not bundle the .NET ecosystem.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"toolchain.discovery\",\n \"level\": \"direct\",\n \"limitations\": \"Static discovery never launches tools; local import does not grant redistribution rights.\",\n \"requires\": [\n \"local directory or ZIP\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"archive.bsarch\",\n \"level\": \"direct\",\n \"limitations\": \"Pack input and all outputs are confined to the Forge workspace; runtime game loading remains separate evidence.\",\n \"requires\": [\n \"configured hash-pinned BSArch\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.decompile\",\n \"level\": \"direct\",\n \"limitations\": \"Decompiler output is not authoritative original source and conveys no redistribution rights.\",\n \"requires\": [\n \"configured hash-pinned Champollion\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"synthesis.cli\",\n \"level\": \"direct\",\n \"limitations\": \"Synthesis.exe GUI is not accepted as the CLI. Patcher-specific runtime behavior requires output verification.\",\n \"requires\": [\n \"Synthesis.Bethesda.CLI executable\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"mesh.deadmesh\",\n \"level\": \"direct\",\n \"limitations\": \"Static collision findings require mesh and runtime review.\",\n \"requires\": [\n \"configured hash-pinned dmscan with sidecars\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"assets.archive\",\n \"level\": \"adapter\",\n \"limitations\": \"BSA/BA2 creation requires a pinned Archive/BSArch worker; archive acceptance is not asset correctness.\",\n \"outputs\": [\n \"safe inventory\",\n \"hashes\",\n \"deterministic ZIP\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"assets.mesh_texture\",\n \"level\": \"worker_contract\",\n \"limitations\": \"NIF, DDS, parallax, PBR, and optimizer transforms require version-specific tools and visual review.\",\n \"requires\": [\n \"pinned asset worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"animation.behavior\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Pandora/Nemesis/OAR/HKX generation is tool-version specific and requires in-game animation review.\",\n \"requires\": [\n \"pinned animation worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"bodyslide\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Batch build can be confined and inventoried, but body/physics output needs visual and collision testing.\",\n \"requires\": [\n \"pinned BodySlide worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"lod\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Worldspace/load-order-specific generation and visual inspection remain mandatory.\",\n \"requires\": [\n \"pinned xLODGen/DynDOLOD worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"grass_cache\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Generation depends on runtime, load order, worldspaces, and stability; Forge does not bundle NGIO or game files.\",\n \"requires\": [\n \"pinned grass-cache worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"audio.voice\",\n \"level\": \"worker_contract\",\n \"limitations\": \"FUZ/LIP/voice generation requires legal source audio, tool-specific processing, and in-game dialogue timing review.\",\n \"requires\": [\n \"pinned audio worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"facegen\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Generated NIF/DDS identity and appearance require xEdit verification and in-game visual review.\",\n \"requires\": [\n \"pinned Creation Kit/CKPE worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"quest.dialogue\",\n \"level\": \"worker_contract\",\n \"limitations\": \"CK fragment/SEQ/voice manifests may be automated by a pinned worker; quest flow, aliases, scenes, and dialogue timing require CK and runtime review.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"worldspace.landscape\",\n \"level\": \"human_gate\",\n \"limitations\": \"Reference placement, landscape, lighting, occlusion, and seams are not safely judged by generic static automation.\",\n \"status\": \"not_automated\"\n },\n {\n \"id\": \"navmesh.write\",\n \"level\": \"unsupported\",\n \"limitations\": \"No generic NAVM writer is exposed. CK finalization and in-game traversal checks remain required.\",\n \"status\": \"blocked\"\n }\n ],\n \"product\": \"Skyrim Forge\",\n \"result\": \"PASS\",\n \"rule\": \"An AI must not claim a stronger evidence level than the capability registry reports.\",\n \"version\": \"5.2.1\"\n },\n \"fomod\": {\n \"assertions\": {\n \"generated_xml\": true,\n \"strict_validation\": true,\n \"typed_plan\": true\n },\n \"result\": \"PASS\",\n \"validation\": {\n \"counts\": {\n \"conditional_patterns\": 0,\n \"dependencies\": 0,\n \"groups\": 0,\n \"mappings\": 1,\n \"plugins\": 0,\n \"steps\": 0\n },\n \"defined_flags\": [],\n \"destination_collisions\": [],\n \"errors\": [],\n \"evidence\": \"FOMOD ModuleConfig 5.0 schema-shape, structural, semantic, source-coverage, destination-collision, temporal flag-reference, and XML-order validation. Actual Vortex/MO2 execution remains an external gate.\",\n \"info_xml\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-znga87so\\\\source\\\\fomod\\\\info.xml\",\n \"missing_sources\": [],\n \"module_config\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-znga87so\\\\source\\\\fomod\\\\ModuleConfig.xml\",\n \"referenced_flags\": [],\n \"result\": \"PASS\",\n \"root\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-znga87so\\\\source\",\n \"unreferenced_payload\": [],\n \"warnings\": []\n }\n },\n \"framework_builder\": {\n \"assertions\": {\n \"bos\": true,\n \"flm\": true,\n \"kid\": true,\n \"skypatcher\": true,\n \"spid\": true\n },\n \"result\": \"PASS\"\n },\n \"framework_lint\": {\n \"assertions\": {\n \"bos_whitespace_rejected\": true,\n \"kid_exclusive_and_type\": true,\n \"kid_signature_rejected\": true,\n \"spid_actor_and_skill_range\": true,\n \"spid_single_value_skill_rejected\": true,\n \"spid_special_keys\": true\n },\n \"result\": \"PASS\"\n },\n \"native\": {\n \"assertions\": {\n \"scaffold_and_audit\": true\n },\n \"result\": \"PASS\"\n },\n \"nexus\": {\n \"files\": 2,\n \"result\": \"PASS\",\n \"share_ready\": true\n },\n \"papyrus\": {\n \"assertions\": {\n \"hot_event_warning\": true,\n \"valid_identity\": true\n },\n \"result\": \"PASS\"\n },\n \"toolchain\": {\n \"catalog_count\": 28,\n \"result\": \"PASS\"\n }\n },\n \"evidence\": \"Built-in deterministic regression fixtures. External tool and Skyrim runtime evidence remain separate.\",\n \"failed\": [],\n \"result\": \"PASS\",\n \"version\": \"5.2.1\"\n}\n" + "stdout": "{\n \"checks\": {\n \"capabilities\": {\n \"capabilities\": [\n {\n \"id\": \"plugin.inspect\",\n \"level\": \"direct\",\n \"limitations\": \"Forge's parser is independent of xEdit but is not a replacement for xEdit or runtime validation.\",\n \"outputs\": [\n \"headers\",\n \"masters\",\n \"record queries\",\n \"hashes\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"plugin.write.low_risk\",\n \"level\": \"direct\",\n \"limitations\": \"Typed records only; no arbitrary binary record editing.\",\n \"outputs\": [\n \"KYWD\",\n \"GLOB\",\n \"FLST\",\n \"OTFT\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"framework.lint\",\n \"level\": \"profiled\",\n \"limitations\": \"Unknown or version-specific syntax is reported as unverified, not automatically rewritten.\",\n \"profiles\": [\n \"SPID 7.3\",\n \"KID 4.0.6\",\n \"BOS 3.4.1\",\n \"SkyPatcher 6.4.2 placement/core rules\",\n \"FLM 1.8.1 core rules\",\n \"CDF pinned JSON subset\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"framework.build\",\n \"level\": \"profiled\",\n \"limitations\": \"Generates only modeled subsets and validates the generated file. It does not invent category-specific SkyPatcher properties.\",\n \"profiles\": [\n \"SPID 7.3\",\n \"KID 4.0.6\",\n \"BOS 3.4.1\",\n \"SkyPatcher 6.4.2 core INI\",\n \"FLM 1.8.1 core INI\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.analyze\",\n \"level\": \"profiled\",\n \"limitations\": \"Performance findings are review warnings, never automatic semantic rewrites.\",\n \"outputs\": [\n \"identity graph\",\n \"inheritance cycles\",\n \"import conflicts\",\n \"performance heuristics\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.compile\",\n \"level\": \"adapter\",\n \"limitations\": \"Compilation proves compiler acceptance and fresh PEX output, not runtime correctness.\",\n \"requires\": [\n \"pinned Bethesda PapyrusCompiler\",\n \"flags file\",\n \"source imports\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.scaffold\",\n \"level\": \"profiled\",\n \"limitations\": \"The scaffold is source-locked; installed compilers and dependencies remain external.\",\n \"outputs\": [\n \"CommonLibSSE-NG CMake project\",\n \"vcpkg manifests\",\n \"SKSE entry point\",\n \"CI workflow\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.build\",\n \"level\": \"adapter\",\n \"limitations\": \"Builds only inside the Forge workspace and does not deploy to live Data.\",\n \"requires\": [\n \"pinned CMake\",\n \"pinned vcpkg\",\n \"Visual Studio C++ toolchain\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"native.binary_audit\",\n \"level\": \"direct\",\n \"limitations\": \"Static PE evidence does not prove compatibility with every SKSE plugin or runtime.\",\n \"outputs\": [\n \"PE architecture\",\n \"DLL characteristic\",\n \"imports\",\n \"hash\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"fomod.engineering\",\n \"level\": \"profiled\",\n \"limitations\": \"Declarative ModuleConfig only; arbitrary C# installers are blocked.\",\n \"outputs\": [\n \"typed plan\",\n \"ModuleConfig XML\",\n \"simulation\",\n \"release gate\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"xedit.automation\",\n \"level\": \"adapter\",\n \"limitations\": \"The xEdit process may show a window, but jobs require completion markers and no user interaction.\",\n \"requires\": [\n \"installed hash-pinned xEdit\",\n \"allowlisted Pascal scripts\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"creation_kit\",\n \"level\": \"worker_contract\",\n \"limitations\": \"No universal headless CK bridge is bundled. UI automation is narrow and coordinate-free.\",\n \"requires\": [\n \"version-pinned local CK or CKPE worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"wrye_bash\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Requires a version-pinned worker around the installed Wrye Bash version.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"loot\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Forge validates and applies typed plans; it does not invent unsupported LOOT GUI arguments.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"mo2\",\n \"level\": \"adapter\",\n \"limitations\": \"Profile capture and bounded launching only; live profile changes require approval.\",\n \"status\": \"implemented\"\n },\n {\n \"id\": \"vortex\",\n \"level\": \"adapter\",\n \"limitations\": \"Forge inspects staging and builds archives. It does not mutate Vortex's internal database.\",\n \"status\": \"read_only\"\n },\n {\n \"id\": \"nexus.publication\",\n \"level\": \"human_gate\",\n \"limitations\": \"Forge enforces machine-checkable declarations and evidence completeness, but cannot authenticate legal ownership, consent, trademark/privacy clearance, or permission conversations. The uploader must attest and remains responsible.\",\n \"outputs\": [\n \"rights manifest\",\n \"credits\",\n \"third-party notices\",\n \"permissions\",\n \"AI disclosure\",\n \"private evidence audit\",\n \"Nexus BBCode page\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"runtime.skyrim\",\n \"level\": \"human_gate\",\n \"limitations\": \"Crashes, visuals, balance, navmesh, save migration, hooks, animation quality, and gameplay require real runtime testing.\",\n \"status\": \"required\"\n },\n {\n \"id\": \"synthesis\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Requires a pinned local Synthesis worker and patcher source; Forge can confine outputs and receipts but does not bundle the .NET ecosystem.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"toolchain.discovery\",\n \"level\": \"direct\",\n \"limitations\": \"Static discovery never launches tools; local import does not grant redistribution rights.\",\n \"requires\": [\n \"local directory or ZIP\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"archive.bsarch\",\n \"level\": \"direct\",\n \"limitations\": \"Pack input and all outputs are confined to the Forge workspace; runtime game loading remains separate evidence.\",\n \"requires\": [\n \"configured hash-pinned BSArch\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"papyrus.decompile\",\n \"level\": \"direct\",\n \"limitations\": \"Decompiler output is not authoritative original source and conveys no redistribution rights.\",\n \"requires\": [\n \"configured hash-pinned Champollion\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"synthesis.cli\",\n \"level\": \"direct\",\n \"limitations\": \"Synthesis.exe GUI is not accepted as the CLI. Patcher-specific runtime behavior requires output verification.\",\n \"requires\": [\n \"Synthesis.Bethesda.CLI executable\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"mesh.deadmesh\",\n \"level\": \"direct\",\n \"limitations\": \"Static collision findings require mesh and runtime review.\",\n \"requires\": [\n \"configured hash-pinned dmscan with sidecars\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"assets.archive\",\n \"level\": \"adapter\",\n \"limitations\": \"BSA/BA2 creation requires a pinned Archive/BSArch worker; archive acceptance is not asset correctness.\",\n \"outputs\": [\n \"safe inventory\",\n \"hashes\",\n \"deterministic ZIP\"\n ],\n \"status\": \"implemented\"\n },\n {\n \"id\": \"assets.mesh_texture\",\n \"level\": \"worker_contract\",\n \"limitations\": \"NIF, DDS, parallax, PBR, and optimizer transforms require version-specific tools and visual review.\",\n \"requires\": [\n \"pinned asset worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"animation.behavior\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Pandora/Nemesis/OAR/HKX generation is tool-version specific and requires in-game animation review.\",\n \"requires\": [\n \"pinned animation worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"bodyslide\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Batch build can be confined and inventoried, but body/physics output needs visual and collision testing.\",\n \"requires\": [\n \"pinned BodySlide worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"lod\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Worldspace/load-order-specific generation and visual inspection remain mandatory.\",\n \"requires\": [\n \"pinned xLODGen/DynDOLOD worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"grass_cache\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Generation depends on runtime, load order, worldspaces, and stability; Forge does not bundle NGIO or game files.\",\n \"requires\": [\n \"pinned grass-cache worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"audio.voice\",\n \"level\": \"worker_contract\",\n \"limitations\": \"FUZ/LIP/voice generation requires legal source audio, tool-specific processing, and in-game dialogue timing review.\",\n \"requires\": [\n \"pinned audio worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"facegen\",\n \"level\": \"worker_contract\",\n \"limitations\": \"Generated NIF/DDS identity and appearance require xEdit verification and in-game visual review.\",\n \"requires\": [\n \"pinned Creation Kit/CKPE worker\"\n ],\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"quest.dialogue\",\n \"level\": \"worker_contract\",\n \"limitations\": \"CK fragment/SEQ/voice manifests may be automated by a pinned worker; quest flow, aliases, scenes, and dialogue timing require CK and runtime review.\",\n \"status\": \"adapter_only\"\n },\n {\n \"id\": \"worldspace.landscape\",\n \"level\": \"human_gate\",\n \"limitations\": \"Reference placement, landscape, lighting, occlusion, and seams are not safely judged by generic static automation.\",\n \"status\": \"not_automated\"\n },\n {\n \"id\": \"navmesh.write\",\n \"level\": \"unsupported\",\n \"limitations\": \"No generic NAVM writer is exposed. CK finalization and in-game traversal checks remain required.\",\n \"status\": \"blocked\"\n }\n ],\n \"product\": \"Skyrim Forge\",\n \"result\": \"PASS\",\n \"rule\": \"An AI must not claim a stronger evidence level than the capability registry reports.\",\n \"version\": \"6.0.0\"\n },\n \"fomod\": {\n \"assertions\": {\n \"generated_xml\": true,\n \"strict_validation\": true,\n \"typed_plan\": true\n },\n \"result\": \"PASS\",\n \"validation\": {\n \"counts\": {\n \"conditional_patterns\": 0,\n \"dependencies\": 0,\n \"groups\": 0,\n \"mappings\": 1,\n \"plugins\": 0,\n \"steps\": 0\n },\n \"defined_flags\": [],\n \"destination_collisions\": [],\n \"errors\": [],\n \"evidence\": \"FOMOD ModuleConfig 5.0 schema-shape, structural, semantic, source-coverage, destination-collision, temporal flag-reference, and XML-order validation. Actual Vortex/MO2 execution remains an external gate.\",\n \"info_xml\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-_ikc5_r0\\\\source\\\\fomod\\\\info.xml\",\n \"missing_sources\": [],\n \"module_config\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-_ikc5_r0\\\\source\\\\fomod\\\\ModuleConfig.xml\",\n \"referenced_flags\": [],\n \"result\": \"PASS\",\n \"root\": \"C:\\\\Users\\\\karlo\\\\AppData\\\\Local\\\\Temp\\\\forge-fomod-self-test-_ikc5_r0\\\\source\",\n \"unreferenced_payload\": [],\n \"warnings\": []\n }\n },\n \"framework_builder\": {\n \"assertions\": {\n \"bos\": true,\n \"flm\": true,\n \"kid\": true,\n \"skypatcher\": true,\n \"spid\": true\n },\n \"result\": \"PASS\"\n },\n \"framework_lint\": {\n \"assertions\": {\n \"bos_whitespace_rejected\": true,\n \"kid_exclusive_and_type\": true,\n \"kid_signature_rejected\": true,\n \"spid_actor_and_skill_range\": true,\n \"spid_single_value_skill_rejected\": true,\n \"spid_special_keys\": true\n },\n \"result\": \"PASS\"\n },\n \"native\": {\n \"assertions\": {\n \"scaffold_and_audit\": true\n },\n \"result\": \"PASS\"\n },\n \"nexus\": {\n \"files\": 2,\n \"result\": \"PASS\",\n \"share_ready\": true\n },\n \"papyrus\": {\n \"assertions\": {\n \"hot_event_warning\": true,\n \"valid_identity\": true\n },\n \"result\": \"PASS\"\n },\n \"toolchain\": {\n \"catalog_count\": 28,\n \"result\": \"PASS\"\n }\n },\n \"evidence\": \"Built-in deterministic regression fixtures. External tool and Skyrim runtime evidence remain separate.\",\n \"failed\": [],\n \"result\": \"PASS\",\n \"version\": \"6.0.0\"\n}\n" }, - "test_count": 162, + "test_count": 159, "tests": { "command": [ - "\\.venv\\Scripts\\python.exe", + "\\AppData\\Local\\Python\\pythoncore-3.14-64\\python.exe", "-m", "unittest", "discover", @@ -267,7 +267,7 @@ "-v" ], "returncode": 0, - "stderr": "test_automation_framework_job (test_automation.AutomationTests.test_automation_framework_job) ... ok\ntest_external_worker_contract (test_automation.AutomationTests.test_external_worker_contract) ... ok\ntest_external_worker_forces_output_root_and_rejects_escape (test_automation.AutomationTests.test_external_worker_forces_output_root_and_rejects_escape) ... ok\ntest_invalid_windows_exe_is_rejected_before_launch (test_automation.AutomationTests.test_invalid_windows_exe_is_rejected_before_launch) ... ok\ntest_loot_apply_only_allows_configured_profile_targets (test_automation.AutomationTests.test_loot_apply_only_allows_configured_profile_targets) ... ok\ntest_loot_plan_comparison (test_automation.AutomationTests.test_loot_plan_comparison) ... ok\ntest_mo2_command_forbids_multiple (test_automation.AutomationTests.test_mo2_command_forbids_multiple) ... ok\ntest_profile_snapshot (test_automation.AutomationTests.test_profile_snapshot) ... ok\ntest_python_fixture_uses_current_interpreter (test_automation.AutomationTests.test_python_fixture_uses_current_interpreter) ... ok\ntest_ui_job_rejects_coordinates_and_ocr (test_automation.AutomationTests.test_ui_job_rejects_coordinates_and_ocr) ... ok\ntest_unknown_automation_fields_rejected (test_automation.AutomationTests.test_unknown_automation_fields_rejected) ... ok\ntest_xedit_command_and_marker (test_automation.AutomationTests.test_xedit_command_and_marker) ... ok\ntest_xedit_fixed_script_executes_without_user_input (test_automation.AutomationTests.test_xedit_fixed_script_executes_without_user_input) ... ok\ntest_cli_contract_is_machine_readable_and_exit_code_tracks_result (test_bundle_contract_v520.BundleContractTests.test_cli_contract_is_machine_readable_and_exit_code_tracks_result) ... ok\ntest_next_bundle_major_requires_new_contract (test_bundle_contract_v520.BundleContractTests.test_next_bundle_major_requires_new_contract) ... ok\ntest_pre_7_8_bundle_is_rejected (test_bundle_contract_v520.BundleContractTests.test_pre_7_8_bundle_is_rejected) ... ok\ntest_product_version_is_the_5_2_contract_series (test_bundle_contract_v520.BundleContractTests.test_product_version_is_the_5_2_contract_series) ... ok\ntest_ultimate_bundle_7_8_0_is_compatible (test_bundle_contract_v520.BundleContractTests.test_ultimate_bundle_7_8_0_is_compatible) ... ok\ntest_full_scope_contains_reproducibility_checks (test_ci_validation.CIValidationScopeTests.test_full_scope_contains_reproducibility_checks) ... ok\ntest_python_scope_never_rebuilds_native_binaries (test_ci_validation.CIValidationScopeTests.test_python_scope_never_rebuilds_native_binaries) ... ok\ntest_unknown_scope_is_rejected (test_ci_validation.CIValidationScopeTests.test_unknown_scope_is_rejected) ... ok\ntest_codeql_action_versions_are_pinned_together (test_ci_validation.WorkflowPinningTests.test_codeql_action_versions_are_pinned_together) ... ok\ntest_dependabot_groups_the_codeql_pair (test_ci_validation.WorkflowPinningTests.test_dependabot_groups_the_codeql_pair) ... ok\ntest_release_publish_is_idempotent (test_ci_validation.WorkflowPinningTests.test_release_publish_is_idempotent) ... ok\ntest_config_first_use_creates_workspace (test_core.CoreTests.test_config_first_use_creates_workspace) ... ok\ntest_configured_seven_zip_is_an_allowed_read_target (test_core.CoreTests.test_configured_seven_zip_is_an_allowed_read_target) ... ok\ntest_current_papyrus_defaults_roundtrip_without_repeated_migration (test_core.CoreTests.test_current_papyrus_defaults_roundtrip_without_repeated_migration) ... ok\ntest_empty_documents_workspace_migrates_to_install_root (test_core.CoreTests.test_empty_documents_workspace_migrates_to_install_root) ... ok\ntest_framework_regressions (test_core.CoreTests.test_framework_regressions) ... ok\ntest_legacy_papyrus_config_migrates_and_backs_up (test_core.CoreTests.test_legacy_papyrus_config_migrates_and_backs_up) ... ok\ntest_native_module_parses_with_python_311_grammar (test_core.CoreTests.test_native_module_parses_with_python_311_grammar) ... ok\ntest_plugin_build_and_query (test_core.CoreTests.test_plugin_build_and_query) ... ok\ntest_plugin_plan_rejects_unknown_record (test_core.CoreTests.test_plugin_plan_rejects_unknown_record) ... ok\ntest_private_release_can_never_claim_share_ready (test_core.CoreTests.test_private_release_can_never_claim_share_ready) ... ok\ntest_record_query_rejects_unbounded_limit (test_core.CoreTests.test_record_query_rejects_unbounded_limit) ... ok\ntest_release_rejects_private_windows_user_path (test_core.CoreTests.test_release_rejects_private_windows_user_path) ... ok\ntest_release_rejects_symlink (test_core.CoreTests.test_release_rejects_symlink) ... ok\ntest_release_validation_and_determinism (test_core.CoreTests.test_release_validation_and_determinism) ... ok\ntest_strict_json_rejects_duplicates_and_nonfinite (test_core.CoreTests.test_strict_json_rejects_duplicates_and_nonfinite) ... ok\ntest_user_workspace_follows_skyrim_forge_root_not_documents (test_core.CoreTests.test_user_workspace_follows_skyrim_forge_root_not_documents) ... ok\ntest_version_and_self_test_do_not_load_broken_config (test_core.CoreTests.test_version_and_self_test_do_not_load_broken_config) ... ok\ntest_workspace_escape_and_symlink_rejected (test_core.CoreTests.test_workspace_escape_and_symlink_rejected) ... ok\ntest_empty_files_node_is_accepted_for_informational_option (test_fomod.ExistingFomodCompatibilityTests.test_empty_files_node_is_accepted_for_informational_option) ... ok\ntest_empty_info_xml_is_valid_metadata_omission_with_warnings (test_fomod.ExistingFomodCompatibilityTests.test_empty_info_xml_is_valid_metadata_omission_with_warnings) ... ok\ntest_existing_xml_rejects_flag_reference_before_defining_step (test_fomod.ExistingFomodCompatibilityTests.test_existing_xml_rejects_flag_reference_before_defining_step) ... ok\ntest_always_install_and_install_if_usable_apply_when_option_unselected (test_fomod.FomodSchemaHardeningTests.test_always_install_and_install_if_usable_apply_when_option_unselected) ... ok\ntest_always_install_breaks_mutually_exclusive_collision_safety (test_fomod.FomodSchemaHardeningTests.test_always_install_breaks_mutually_exclusive_collision_safety) ... ok\ntest_csharp_installer_is_still_refused (test_fomod.FomodSchemaHardeningTests.test_csharp_installer_is_still_refused) ... ok\ntest_existing_xml_omitted_destination_defaults_to_source (test_fomod.FomodSchemaHardeningTests.test_existing_xml_omitted_destination_defaults_to_source) ... ok\ntest_generated_xml_uses_direct_visible_dependencies_and_schema_order (test_fomod.FomodSchemaHardeningTests.test_generated_xml_uses_direct_visible_dependencies_and_schema_order) ... ok\ntest_plugin_and_conditional_payload_requirements_are_enforced (test_fomod.FomodSchemaHardeningTests.test_plugin_and_conditional_payload_requirements_are_enforced) ... ok\ntest_schema_location_is_reported_but_never_fatal (test_fomod.FomodSchemaHardeningTests.test_schema_location_is_reported_but_never_fatal)\n`xsi:noNamespaceSchemaLocation` is an optional XML hint. ... ok\ntest_temporal_flag_references_are_enforced (test_fomod.FomodSchemaHardeningTests.test_temporal_flag_references_are_enforced) ... ok\ntest_typed_root_destination_is_emitted_explicitly (test_fomod.FomodSchemaHardeningTests.test_typed_root_destination_is_emitted_explicitly) ... ok\ntest_utf16_and_screenshot_module_image_are_supported (test_fomod.FomodSchemaHardeningTests.test_utf16_and_screenshot_module_image_are_supported) ... ok\ntest_equal_priority_collision_is_rejected (test_fomod.FomodTests.test_equal_priority_collision_is_rejected) ... ok\ntest_invalid_moduleconfig_order_is_rejected (test_fomod.FomodTests.test_invalid_moduleconfig_order_is_rejected) ... ok\ntest_path_traversal_is_rejected (test_fomod.FomodTests.test_path_traversal_is_rejected) ... ok\ntest_plan_build_validate_and_release_gate (test_fomod.FomodTests.test_plan_build_validate_and_release_gate) ... ok\ntest_scaffold_unwraps_single_data_folder (test_fomod.FomodTests.test_scaffold_unwraps_single_data_folder) ... ok\ntest_spid_style_branch_simulation_enforces_group_and_flags (test_fomod.FomodTests.test_spid_style_branch_simulation_enforces_group_and_flags) ... ok\ntest_unreferenced_payload_is_rejected (test_fomod.FomodTests.test_unreferenced_payload_is_rejected) ... ok\ntest_game_specific_version_dependency_is_recorded_not_refused (test_fomod.ThirdPartyFomodFalsePositiveTests.test_game_specific_version_dependency_is_recorded_not_refused) ... ok\ntest_plugin_with_an_option_image_is_accepted (test_fomod.ThirdPartyFomodFalsePositiveTests.test_plugin_with_an_option_image_is_accepted) ... ok\ntest_unreferenced_payload_names_the_files_it_rejects (test_fomod.ThirdPartyFomodFalsePositiveTests.test_unreferenced_payload_names_the_files_it_rejects) ... ok\ntest_kid_keys_are_case_insensitive (test_frameworks_spid.InstalledCorpusRegressionTests.test_kid_keys_are_case_insensitive) ... ok\ntest_kid_two_field_line_is_not_an_unsupported_type (test_frameworks_spid.InstalledCorpusRegressionTests.test_kid_two_field_line_is_not_an_unsupported_type) ... ok\ntest_repeated_advisory_is_collapsed_to_one_entry (test_frameworks_spid.InstalledCorpusRegressionTests.test_repeated_advisory_is_collapsed_to_one_entry) ... ok\ntest_skypatcher_categories_present_in_real_installs_are_known (test_frameworks_spid.InstalledCorpusRegressionTests.test_skypatcher_categories_present_in_real_installs_are_known) ... ok\ntest_skypatcher_clause_value_may_be_a_comma_separated_form_list (test_frameworks_spid.InstalledCorpusRegressionTests.test_skypatcher_clause_value_may_be_a_comma_separated_form_list) ... ok\ntest_stacked_and_midfile_boms_do_not_invent_findings (test_frameworks_spid.InstalledCorpusRegressionTests.test_stacked_and_midfile_boms_do_not_invent_findings) ... ok\ntest_actor_level_plus_skill_range_is_valid (test_frameworks_spid.SpidGrammarTests.test_actor_level_plus_skill_range_is_valid) ... ok\ntest_known_weapon_alias_remains_a_hard_error (test_frameworks_spid.SpidGrammarTests.test_known_weapon_alias_remains_a_hard_error) ... ok\ntest_runtime_proven_skill_filter_is_silent (test_frameworks_spid.SpidGrammarTests.test_runtime_proven_skill_filter_is_silent) ... ok\ntest_single_value_skill_expression_is_reported_not_failed (test_frameworks_spid.SpidGrammarTests.test_single_value_skill_expression_is_reported_not_failed)\nA single-value skill filter is reported, never failed. ... ok\ntest_skill_index_and_range_order_are_checked (test_frameworks_spid.SpidGrammarTests.test_skill_index_and_range_order_are_checked) ... ok\ntest_unknown_future_key_is_warning_not_false_failure (test_frameworks_spid.SpidGrammarTests.test_unknown_future_key_is_warning_not_false_failure) ... ok\ntest_both_eras_expose_the_same_tools (test_mcp.DualEraMcpTests.test_both_eras_expose_the_same_tools) ... ok\ntest_discover_answers_an_unprobed_client (test_mcp.DualEraMcpTests.test_discover_answers_an_unprobed_client) ... ok\ntest_initialize_modern_version_carries_result_type (test_mcp.DualEraMcpTests.test_initialize_modern_version_carries_result_type) ... ok\ntest_legacy_clients_keep_their_exact_result_shape (test_mcp.DualEraMcpTests.test_legacy_clients_keep_their_exact_result_shape) ... ok\ntest_legacy_initialize_keeps_handshake_shape (test_mcp.DualEraMcpTests.test_legacy_initialize_keeps_handshake_shape) ... ok\ntest_local_configuration_is_never_publicly_cacheable (test_mcp.DualEraMcpTests.test_local_configuration_is_never_publicly_cacheable) ... ok\ntest_modern_lists_carry_required_caching_hints (test_mcp.DualEraMcpTests.test_modern_lists_carry_required_caching_hints) ... ok\ntest_modern_tool_call_carries_result_type (test_mcp.DualEraMcpTests.test_modern_tool_call_carries_result_type) ... ok\ntest_server_discover_is_implemented (test_mcp.DualEraMcpTests.test_server_discover_is_implemented) ... ok\ntest_tool_call_without_meta_still_carries_result_type (test_mcp.DualEraMcpTests.test_tool_call_without_meta_still_carries_result_type) ... ok\ntest_unsupported_version_is_refused_with_its_supported_list (test_mcp.DualEraMcpTests.test_unsupported_version_is_refused_with_its_supported_list) ... ok\ntest_fomod_surface_is_exposed (test_mcp.McpTests.test_fomod_surface_is_exposed) ... ok\ntest_initialize_and_tools (test_mcp.McpTests.test_initialize_and_tools) ... ok\ntest_nexus_publication_surface_is_exposed (test_mcp.McpTests.test_nexus_publication_surface_is_exposed) ... ok\ntest_papyrus_compile_tool_reaches_the_service (test_mcp.McpTests.test_papyrus_compile_tool_reaches_the_service) ... ok\ntest_tool_call (test_mcp.McpTests.test_tool_call) ... ok\ntest_packaged_native_helpers_match_published_helpers (test_native_release_integrity.NativeReleaseIntegrityTests.test_packaged_native_helpers_match_published_helpers) ... ok\ntest_git_checkout_build_matches_bundled_helper (test_native_vcs_reproducibility.NativeVCSReproducibilityTests.test_git_checkout_build_matches_bundled_helper) ... skipped 'Exact release toolchain unavailable: go1.26.5'\ntest_release_build_paths_disable_vcs_metadata (test_native_vcs_reproducibility.NativeVCSReproducibilityTests.test_release_build_paths_disable_vcs_metadata) ... ok\ntest_absolute_permission_evidence_path_rejected (test_nexus_publication.NexusPublicationTests.test_absolute_permission_evidence_path_rejected) ... ok\ntest_adult_content_requires_matching_tag (test_nexus_publication.NexusPublicationTests.test_adult_content_requires_matching_tag) ... ok\ntest_build_bundle_separates_private_audit (test_nexus_publication.NexusPublicationTests.test_build_bundle_separates_private_audit) ... ok\ntest_compilation_requires_value_beyond_repackaging (test_nexus_publication.NexusPublicationTests.test_compilation_requires_value_beyond_repackaging) ... ok\ntest_copyleft_license_requires_source_and_acknowledgement (test_nexus_publication.NexusPublicationTests.test_copyleft_license_requires_source_and_acknowledgement) ... ok\ntest_credit_is_not_permission (test_nexus_publication.NexusPublicationTests.test_credit_is_not_permission) ... ok\ntest_donation_points_requires_each_asset_permission (test_nexus_publication.NexusPublicationTests.test_donation_points_requires_each_asset_permission) ... ok\ntest_event_profile_blocks_ai (test_nexus_publication.NexusPublicationTests.test_event_profile_blocks_ai) ... ok\ntest_executable_inventory_must_match_declaration (test_nexus_publication.NexusPublicationTests.test_executable_inventory_must_match_declaration) ... ok\ntest_explicit_permission_requires_evidence (test_nexus_publication.NexusPublicationTests.test_explicit_permission_requires_evidence) ... ok\ntest_internet_connected_tool_requires_source_staff_contact_and_disclosure (test_nexus_publication.NexusPublicationTests.test_internet_connected_tool_requires_source_staff_contact_and_disclosure) ... ok\ntest_known_game_file_is_blocked (test_nexus_publication.NexusPublicationTests.test_known_game_file_is_blocked) ... ok\ntest_multiple_asset_match_blocks_release (test_nexus_publication.NexusPublicationTests.test_multiple_asset_match_blocks_release) ... ok\ntest_nexus_permission_presets_cannot_exceed_asset_rights (test_nexus_publication.NexusPublicationTests.test_nexus_permission_presets_cannot_exceed_asset_rights) ... ok\ntest_open_license_requires_licence_copy (test_nexus_publication.NexusPublicationTests.test_open_license_requires_licence_copy) ... ok\ntest_patch_release_requires_original_dependency (test_nexus_publication.NexusPublicationTests.test_patch_release_requires_original_dependency) ... ok\ntest_performance_claim_requires_evidence (test_nexus_publication.NexusPublicationTests.test_performance_claim_requires_evidence) ... ok\ntest_relative_permission_evidence_is_hashed_but_not_exposed (test_nexus_publication.NexusPublicationTests.test_relative_permission_evidence_is_hashed_but_not_exposed) ... ok\ntest_rendered_page_contains_credits_permissions_and_ai_disclosure (test_nexus_publication.NexusPublicationTests.test_rendered_page_contains_credits_permissions_and_ai_disclosure) ... ok\ntest_stale_policy_review_blocks (test_nexus_publication.NexusPublicationTests.test_stale_policy_review_blocks) ... ok\ntest_unknown_license_requires_manual_acknowledgement (test_nexus_publication.NexusPublicationTests.test_unknown_license_requires_manual_acknowledgement) ... ok\ntest_unmapped_file_blocks_release (test_nexus_publication.NexusPublicationTests.test_unmapped_file_blocks_release) ... ok\ntest_uploader_attestation_cannot_be_mismatched (test_nexus_publication.NexusPublicationTests.test_uploader_attestation_cannot_be_mismatched) ... ok\ntest_valid_release_is_share_ready (test_nexus_publication.NexusPublicationTests.test_valid_release_is_share_ready) ... ok\ntest_line_endings_match_their_declared_attribute (test_packaging.DistributedIntegrityTests.test_line_endings_match_their_declared_attribute) ... ok\ntest_manifest_verifies_against_the_delivered_tree (test_packaging.DistributedIntegrityTests.test_manifest_verifies_against_the_delivered_tree) ... ok\ntest_no_external_build_requirements (test_packaging.PackagingTests.test_no_external_build_requirements) ... ok\ntest_runtime_installation_artifacts_are_never_packaged (test_packaging.PackagingTests.test_runtime_installation_artifacts_are_never_packaged) ... ok\ntest_wheel_determinism_and_native_files (test_packaging.PackagingTests.test_wheel_determinism_and_native_files) ... ok\ntest_batch_files_never_pass_quoted_dp0_as_a_standalone_external_argument (test_powershell_release.PowerShellReleaseTests.test_batch_files_never_pass_quoted_dp0_as_a_standalone_external_argument) ... ok\ntest_no_expandable_string_has_ambiguous_variable_before_colon (test_powershell_release.PowerShellReleaseTests.test_no_expandable_string_has_ambiguous_variable_before_colon) ... ok\ntest_parser_gate_defaults_to_its_own_directory_and_sanitizes_legacy_quote (test_powershell_release.PowerShellReleaseTests.test_parser_gate_defaults_to_its_own_directory_and_sanitizes_legacy_quote) ... ok\ntest_provider_bridge_uses_exact_shared_runtime (test_powershell_release.PowerShellReleaseTests.test_provider_bridge_uses_exact_shared_runtime) ... ok\ntest_python_bootstrap_is_pinned_and_verified (test_powershell_release.PowerShellReleaseTests.test_python_bootstrap_is_pinned_and_verified) ... ok\ntest_runtime_virtualenv_scripts_are_not_release_sources (test_powershell_release.PowerShellReleaseTests.test_runtime_virtualenv_scripts_are_not_release_sources) ... ok\ntest_skill_installer_is_transactional_and_uses_safe_formatting (test_powershell_release.PowerShellReleaseTests.test_skill_installer_is_transactional_and_uses_safe_formatting) ... ok\ntest_start_menu_uses_gate_default_root_and_has_noninteractive_ci_probe (test_powershell_release.PowerShellReleaseTests.test_start_menu_uses_gate_default_root_and_has_noninteractive_ci_probe) ... ok\ntest_windows_ci_exercises_broken_virtualenv_repair (test_powershell_release.PowerShellReleaseTests.test_windows_ci_exercises_broken_virtualenv_repair) ... ok\ntest_python_script_executes_through_current_interpreter (test_process_launch.ProcessLaunchTests.test_python_script_executes_through_current_interpreter) ... ok\ntest_windows_rejects_text_disguised_as_exe_without_launching (test_process_launch.ProcessLaunchTests.test_windows_rejects_text_disguised_as_exe_without_launching) ... ok\ntest_bsarch_adapter_uses_pinned_real_tool_contract_and_reopens (test_toolchain_v42.ToolchainV42Tests.test_bsarch_adapter_uses_pinned_real_tool_contract_and_reopens) ... ok\ntest_bulk_import_is_idempotent_and_reconfigures_existing_vault_entry (test_toolchain_v42.ToolchainV42Tests.test_bulk_import_is_idempotent_and_reconfigures_existing_vault_entry) ... ok\ntest_catalog_disables_public_third_party_binary_bundling (test_toolchain_v42.ToolchainV42Tests.test_catalog_disables_public_third_party_binary_bundling) ... ok\ntest_configure_existing_rejects_gui_as_cli_by_name (test_toolchain_v42.ToolchainV42Tests.test_configure_existing_rejects_gui_as_cli_by_name) ... ok\ntest_nested_bsarch_is_discovered_and_imported_with_pin (test_toolchain_v42.ToolchainV42Tests.test_nested_bsarch_is_discovered_and_imported_with_pin) ... ok\ntest_scan_does_not_execute_candidates (test_toolchain_v42.ToolchainV42Tests.test_scan_does_not_execute_candidates) ... ok\ntest_synthesis_gui_is_never_resolved_as_cli (test_toolchain_v42.ToolchainV42Tests.test_synthesis_gui_is_never_resolved_as_cli) ... ok\ntest_all_built_in_self_tests_are_integrated (test_v41_completion.V41CompletionTests.test_all_built_in_self_tests_are_integrated) ... ok\ntest_papyrus_compile_uses_pinned_python_mock_and_manifest (test_v41_completion.V41CompletionTests.test_papyrus_compile_uses_pinned_python_mock_and_manifest) ... ok\ntest_v41_mcp_surface_is_complete (test_v41_completion.V41CompletionTests.test_v41_mcp_surface_is_complete) ... ok\ntest_capability_registry_has_honest_boundaries (test_v41_engineering.V41EngineeringTests.test_capability_registry_has_honest_boundaries) ... ok\ntest_framework_builder_flags_single_value_skill_without_failing (test_v41_engineering.V41EngineeringTests.test_framework_builder_flags_single_value_skill_without_failing) ... ok\ntest_framework_builder_spid_runtime_proven_shape (test_v41_engineering.V41EngineeringTests.test_framework_builder_spid_runtime_proven_shape) ... ok\ntest_native_binary_audit_distinguishes_dll (test_v41_engineering.V41EngineeringTests.test_native_binary_audit_distinguishes_dll) ... ok\ntest_native_runtime_strategy_conflicts_are_rejected (test_v41_engineering.V41EngineeringTests.test_native_runtime_strategy_conflicts_are_rejected) ... ok\ntest_native_scaffold_uses_one_runtime_strategy_and_staging (test_v41_engineering.V41EngineeringTests.test_native_scaffold_uses_one_runtime_strategy_and_staging) ... ok\ntest_papyrus_case_identity_and_hot_event_are_reviewed (test_v41_engineering.V41EngineeringTests.test_papyrus_case_identity_and_hot_event_are_reviewed) ... ok\ntest_plugin_formkeys_are_required_and_encoded (test_v41_engineering.V41EngineeringTests.test_plugin_formkeys_are_required_and_encoded) ... ok\ntest_skypatcher_typed_build_uses_canonical_path (test_v41_engineering.V41EngineeringTests.test_skypatcher_typed_build_uses_canonical_path) ... ok\ntest_skypatcher_unknown_category_is_warning_not_false_failure (test_v41_engineering.V41EngineeringTests.test_skypatcher_unknown_category_is_warning_not_false_failure) ... ok\ntest_hermes_registration_uses_supported_cli_and_tests_connection (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_registration_uses_supported_cli_and_tests_connection) ... ok\ntest_hermes_skill_defaults_to_local_app_data (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_skill_defaults_to_local_app_data) ... ok\ntest_hermes_test_failure_restores_original_configuration (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_test_failure_restores_original_configuration) ... ok\ntest_kimi_doctor_failure_restores_original_configuration (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_doctor_failure_restores_original_configuration) ... ok\ntest_kimi_registration_preserves_other_servers_and_writes_forge (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_registration_preserves_other_servers_and_writes_forge) ... ok\ntest_kimi_rejects_array_shaped_server_map_without_overwriting (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_rejects_array_shaped_server_map_without_overwriting) ... ok\n\n----------------------------------------------------------------------\nRan 162 tests in 5.721s\n\nOK (skipped=1)\n", + "stderr": "test_automation_framework_job (test_automation.AutomationTests.test_automation_framework_job) ... ok\ntest_external_worker_contract (test_automation.AutomationTests.test_external_worker_contract) ... ok\ntest_external_worker_forces_output_root_and_rejects_escape (test_automation.AutomationTests.test_external_worker_forces_output_root_and_rejects_escape) ... ok\ntest_invalid_windows_exe_is_rejected_before_launch (test_automation.AutomationTests.test_invalid_windows_exe_is_rejected_before_launch) ... ok\ntest_loot_apply_only_allows_configured_profile_targets (test_automation.AutomationTests.test_loot_apply_only_allows_configured_profile_targets) ... ok\ntest_loot_plan_comparison (test_automation.AutomationTests.test_loot_plan_comparison) ... ok\ntest_mo2_command_forbids_multiple (test_automation.AutomationTests.test_mo2_command_forbids_multiple) ... ok\ntest_profile_snapshot (test_automation.AutomationTests.test_profile_snapshot) ... ok\ntest_python_fixture_uses_current_interpreter (test_automation.AutomationTests.test_python_fixture_uses_current_interpreter) ... ok\ntest_ui_job_rejects_coordinates_and_ocr (test_automation.AutomationTests.test_ui_job_rejects_coordinates_and_ocr) ... ok\ntest_unknown_automation_fields_rejected (test_automation.AutomationTests.test_unknown_automation_fields_rejected) ... ok\ntest_xedit_command_and_marker (test_automation.AutomationTests.test_xedit_command_and_marker) ... ok\ntest_xedit_fixed_script_executes_without_user_input (test_automation.AutomationTests.test_xedit_fixed_script_executes_without_user_input) ... ok\ntest_embedded_rebuild_helper_resolves_root_go_toolchain (test_ci_validation.CIValidationScopeTests.test_embedded_rebuild_helper_resolves_root_go_toolchain) ... ok\ntest_embedded_validator_resolves_root_go_toolchain (test_ci_validation.CIValidationScopeTests.test_embedded_validator_resolves_root_go_toolchain) ... ok\ntest_full_scope_contains_reproducibility_checks (test_ci_validation.CIValidationScopeTests.test_full_scope_contains_reproducibility_checks) ... ok\ntest_python_scope_never_rebuilds_native_binaries (test_ci_validation.CIValidationScopeTests.test_python_scope_never_rebuilds_native_binaries) ... ok\ntest_unknown_scope_is_rejected (test_ci_validation.CIValidationScopeTests.test_unknown_scope_is_rejected) ... ok\ntest_codeql_action_versions_are_pinned_together (test_ci_validation.WorkflowPinningTests.test_codeql_action_versions_are_pinned_together) ... ok\ntest_dependabot_groups_the_codeql_pair (test_ci_validation.WorkflowPinningTests.test_dependabot_groups_the_codeql_pair) ... ok\ntest_release_publish_is_idempotent (test_ci_validation.WorkflowPinningTests.test_release_publish_is_idempotent) ... ok\ntest_config_first_use_creates_workspace (test_core.CoreTests.test_config_first_use_creates_workspace) ... ok\ntest_configured_seven_zip_is_an_allowed_read_target (test_core.CoreTests.test_configured_seven_zip_is_an_allowed_read_target) ... ok\ntest_current_papyrus_defaults_roundtrip_without_repeated_migration (test_core.CoreTests.test_current_papyrus_defaults_roundtrip_without_repeated_migration) ... ok\ntest_empty_documents_workspace_migrates_to_install_root (test_core.CoreTests.test_empty_documents_workspace_migrates_to_install_root) ... ok\ntest_framework_regressions (test_core.CoreTests.test_framework_regressions) ... ok\ntest_legacy_papyrus_config_migrates_and_backs_up (test_core.CoreTests.test_legacy_papyrus_config_migrates_and_backs_up) ... ok\ntest_native_module_parses_with_python_311_grammar (test_core.CoreTests.test_native_module_parses_with_python_311_grammar) ... ok\ntest_plugin_build_and_query (test_core.CoreTests.test_plugin_build_and_query) ... ok\ntest_plugin_plan_rejects_unknown_record (test_core.CoreTests.test_plugin_plan_rejects_unknown_record) ... ok\ntest_private_release_can_never_claim_share_ready (test_core.CoreTests.test_private_release_can_never_claim_share_ready) ... ok\ntest_record_query_rejects_unbounded_limit (test_core.CoreTests.test_record_query_rejects_unbounded_limit) ... ok\ntest_release_rejects_private_windows_user_path (test_core.CoreTests.test_release_rejects_private_windows_user_path) ... ok\ntest_release_rejects_symlink (test_core.CoreTests.test_release_rejects_symlink) ... ok\ntest_release_validation_and_determinism (test_core.CoreTests.test_release_validation_and_determinism) ... ok\ntest_strict_json_rejects_duplicates_and_nonfinite (test_core.CoreTests.test_strict_json_rejects_duplicates_and_nonfinite) ... ok\ntest_user_workspace_follows_skyrim_forge_root_not_documents (test_core.CoreTests.test_user_workspace_follows_skyrim_forge_root_not_documents) ... ok\ntest_version_and_self_test_do_not_load_broken_config (test_core.CoreTests.test_version_and_self_test_do_not_load_broken_config) ... ok\ntest_workspace_escape_and_symlink_rejected (test_core.CoreTests.test_workspace_escape_and_symlink_rejected) ... ok\ntest_empty_files_node_is_accepted_for_informational_option (test_fomod.ExistingFomodCompatibilityTests.test_empty_files_node_is_accepted_for_informational_option) ... ok\ntest_empty_info_xml_is_valid_metadata_omission_with_warnings (test_fomod.ExistingFomodCompatibilityTests.test_empty_info_xml_is_valid_metadata_omission_with_warnings) ... ok\ntest_existing_xml_rejects_flag_reference_before_defining_step (test_fomod.ExistingFomodCompatibilityTests.test_existing_xml_rejects_flag_reference_before_defining_step) ... ok\ntest_always_install_and_install_if_usable_apply_when_option_unselected (test_fomod.FomodSchemaHardeningTests.test_always_install_and_install_if_usable_apply_when_option_unselected) ... ok\ntest_always_install_breaks_mutually_exclusive_collision_safety (test_fomod.FomodSchemaHardeningTests.test_always_install_breaks_mutually_exclusive_collision_safety) ... ok\ntest_csharp_installer_is_still_refused (test_fomod.FomodSchemaHardeningTests.test_csharp_installer_is_still_refused) ... ok\ntest_existing_xml_omitted_destination_defaults_to_source (test_fomod.FomodSchemaHardeningTests.test_existing_xml_omitted_destination_defaults_to_source) ... ok\ntest_generated_xml_uses_direct_visible_dependencies_and_schema_order (test_fomod.FomodSchemaHardeningTests.test_generated_xml_uses_direct_visible_dependencies_and_schema_order) ... ok\ntest_plugin_and_conditional_payload_requirements_are_enforced (test_fomod.FomodSchemaHardeningTests.test_plugin_and_conditional_payload_requirements_are_enforced) ... ok\ntest_schema_location_is_reported_but_never_fatal (test_fomod.FomodSchemaHardeningTests.test_schema_location_is_reported_but_never_fatal)\n`xsi:noNamespaceSchemaLocation` is an optional XML hint. ... ok\ntest_temporal_flag_references_are_enforced (test_fomod.FomodSchemaHardeningTests.test_temporal_flag_references_are_enforced) ... ok\ntest_typed_root_destination_is_emitted_explicitly (test_fomod.FomodSchemaHardeningTests.test_typed_root_destination_is_emitted_explicitly) ... ok\ntest_utf16_and_screenshot_module_image_are_supported (test_fomod.FomodSchemaHardeningTests.test_utf16_and_screenshot_module_image_are_supported) ... ok\ntest_equal_priority_collision_is_rejected (test_fomod.FomodTests.test_equal_priority_collision_is_rejected) ... ok\ntest_invalid_moduleconfig_order_is_rejected (test_fomod.FomodTests.test_invalid_moduleconfig_order_is_rejected) ... ok\ntest_path_traversal_is_rejected (test_fomod.FomodTests.test_path_traversal_is_rejected) ... ok\ntest_plan_build_validate_and_release_gate (test_fomod.FomodTests.test_plan_build_validate_and_release_gate) ... ok\ntest_scaffold_unwraps_single_data_folder (test_fomod.FomodTests.test_scaffold_unwraps_single_data_folder) ... ok\ntest_spid_style_branch_simulation_enforces_group_and_flags (test_fomod.FomodTests.test_spid_style_branch_simulation_enforces_group_and_flags) ... ok\ntest_unreferenced_payload_is_rejected (test_fomod.FomodTests.test_unreferenced_payload_is_rejected) ... ok\ntest_game_specific_version_dependency_is_recorded_not_refused (test_fomod.ThirdPartyFomodFalsePositiveTests.test_game_specific_version_dependency_is_recorded_not_refused) ... ok\ntest_plugin_with_an_option_image_is_accepted (test_fomod.ThirdPartyFomodFalsePositiveTests.test_plugin_with_an_option_image_is_accepted) ... ok\ntest_unreferenced_payload_names_the_files_it_rejects (test_fomod.ThirdPartyFomodFalsePositiveTests.test_unreferenced_payload_names_the_files_it_rejects) ... ok\ntest_kid_keys_are_case_insensitive (test_frameworks_spid.InstalledCorpusRegressionTests.test_kid_keys_are_case_insensitive) ... ok\ntest_kid_two_field_line_is_not_an_unsupported_type (test_frameworks_spid.InstalledCorpusRegressionTests.test_kid_two_field_line_is_not_an_unsupported_type) ... ok\ntest_repeated_advisory_is_collapsed_to_one_entry (test_frameworks_spid.InstalledCorpusRegressionTests.test_repeated_advisory_is_collapsed_to_one_entry) ... ok\ntest_skypatcher_categories_present_in_real_installs_are_known (test_frameworks_spid.InstalledCorpusRegressionTests.test_skypatcher_categories_present_in_real_installs_are_known) ... ok\ntest_skypatcher_clause_value_may_be_a_comma_separated_form_list (test_frameworks_spid.InstalledCorpusRegressionTests.test_skypatcher_clause_value_may_be_a_comma_separated_form_list) ... ok\ntest_stacked_and_midfile_boms_do_not_invent_findings (test_frameworks_spid.InstalledCorpusRegressionTests.test_stacked_and_midfile_boms_do_not_invent_findings) ... ok\ntest_actor_level_plus_skill_range_is_valid (test_frameworks_spid.SpidGrammarTests.test_actor_level_plus_skill_range_is_valid) ... ok\ntest_known_weapon_alias_remains_a_hard_error (test_frameworks_spid.SpidGrammarTests.test_known_weapon_alias_remains_a_hard_error) ... ok\ntest_runtime_proven_skill_filter_is_silent (test_frameworks_spid.SpidGrammarTests.test_runtime_proven_skill_filter_is_silent) ... ok\ntest_single_value_skill_expression_is_reported_not_failed (test_frameworks_spid.SpidGrammarTests.test_single_value_skill_expression_is_reported_not_failed)\nA single-value skill filter is reported, never failed. ... ok\ntest_skill_index_and_range_order_are_checked (test_frameworks_spid.SpidGrammarTests.test_skill_index_and_range_order_are_checked) ... ok\ntest_unknown_future_key_is_warning_not_false_failure (test_frameworks_spid.SpidGrammarTests.test_unknown_future_key_is_warning_not_false_failure) ... ok\ntest_both_eras_expose_the_same_tools (test_mcp.DualEraMcpTests.test_both_eras_expose_the_same_tools) ... ok\ntest_discover_answers_an_unprobed_client (test_mcp.DualEraMcpTests.test_discover_answers_an_unprobed_client) ... ok\ntest_initialize_modern_version_carries_result_type (test_mcp.DualEraMcpTests.test_initialize_modern_version_carries_result_type) ... ok\ntest_legacy_clients_keep_their_exact_result_shape (test_mcp.DualEraMcpTests.test_legacy_clients_keep_their_exact_result_shape) ... ok\ntest_legacy_initialize_keeps_handshake_shape (test_mcp.DualEraMcpTests.test_legacy_initialize_keeps_handshake_shape) ... ok\ntest_local_configuration_is_never_publicly_cacheable (test_mcp.DualEraMcpTests.test_local_configuration_is_never_publicly_cacheable) ... ok\ntest_modern_lists_carry_required_caching_hints (test_mcp.DualEraMcpTests.test_modern_lists_carry_required_caching_hints) ... ok\ntest_modern_tool_call_carries_result_type (test_mcp.DualEraMcpTests.test_modern_tool_call_carries_result_type) ... ok\ntest_server_discover_is_implemented (test_mcp.DualEraMcpTests.test_server_discover_is_implemented) ... ok\ntest_tool_call_without_meta_still_carries_result_type (test_mcp.DualEraMcpTests.test_tool_call_without_meta_still_carries_result_type) ... ok\ntest_unsupported_version_is_refused_with_its_supported_list (test_mcp.DualEraMcpTests.test_unsupported_version_is_refused_with_its_supported_list) ... ok\ntest_fomod_surface_is_exposed (test_mcp.McpTests.test_fomod_surface_is_exposed) ... ok\ntest_initialize_and_tools (test_mcp.McpTests.test_initialize_and_tools) ... ok\ntest_nexus_publication_surface_is_exposed (test_mcp.McpTests.test_nexus_publication_surface_is_exposed) ... ok\ntest_papyrus_compile_tool_reaches_the_service (test_mcp.McpTests.test_papyrus_compile_tool_reaches_the_service) ... ok\ntest_tool_call (test_mcp.McpTests.test_tool_call) ... ok\ntest_packaged_native_helpers_match_published_helpers (test_native_release_integrity.NativeReleaseIntegrityTests.test_packaged_native_helpers_match_published_helpers) ... ok\ntest_git_checkout_build_matches_bundled_helper (test_native_vcs_reproducibility.NativeVCSReproducibilityTests.test_git_checkout_build_matches_bundled_helper) ... skipped 'Exact release toolchain unavailable: go1.26.5'\ntest_release_build_paths_disable_vcs_metadata (test_native_vcs_reproducibility.NativeVCSReproducibilityTests.test_release_build_paths_disable_vcs_metadata) ... ok\ntest_absolute_permission_evidence_path_rejected (test_nexus_publication.NexusPublicationTests.test_absolute_permission_evidence_path_rejected) ... ok\ntest_adult_content_requires_matching_tag (test_nexus_publication.NexusPublicationTests.test_adult_content_requires_matching_tag) ... ok\ntest_build_bundle_separates_private_audit (test_nexus_publication.NexusPublicationTests.test_build_bundle_separates_private_audit) ... ok\ntest_compilation_requires_value_beyond_repackaging (test_nexus_publication.NexusPublicationTests.test_compilation_requires_value_beyond_repackaging) ... ok\ntest_copyleft_license_requires_source_and_acknowledgement (test_nexus_publication.NexusPublicationTests.test_copyleft_license_requires_source_and_acknowledgement) ... ok\ntest_credit_is_not_permission (test_nexus_publication.NexusPublicationTests.test_credit_is_not_permission) ... ok\ntest_donation_points_requires_each_asset_permission (test_nexus_publication.NexusPublicationTests.test_donation_points_requires_each_asset_permission) ... ok\ntest_event_profile_blocks_ai (test_nexus_publication.NexusPublicationTests.test_event_profile_blocks_ai) ... ok\ntest_executable_inventory_must_match_declaration (test_nexus_publication.NexusPublicationTests.test_executable_inventory_must_match_declaration) ... ok\ntest_explicit_permission_requires_evidence (test_nexus_publication.NexusPublicationTests.test_explicit_permission_requires_evidence) ... ok\ntest_internet_connected_tool_requires_source_staff_contact_and_disclosure (test_nexus_publication.NexusPublicationTests.test_internet_connected_tool_requires_source_staff_contact_and_disclosure) ... ok\ntest_known_game_file_is_blocked (test_nexus_publication.NexusPublicationTests.test_known_game_file_is_blocked) ... ok\ntest_multiple_asset_match_blocks_release (test_nexus_publication.NexusPublicationTests.test_multiple_asset_match_blocks_release) ... ok\ntest_nexus_permission_presets_cannot_exceed_asset_rights (test_nexus_publication.NexusPublicationTests.test_nexus_permission_presets_cannot_exceed_asset_rights) ... ok\ntest_open_license_requires_licence_copy (test_nexus_publication.NexusPublicationTests.test_open_license_requires_licence_copy) ... ok\ntest_patch_release_requires_original_dependency (test_nexus_publication.NexusPublicationTests.test_patch_release_requires_original_dependency) ... ok\ntest_performance_claim_requires_evidence (test_nexus_publication.NexusPublicationTests.test_performance_claim_requires_evidence) ... ok\ntest_relative_permission_evidence_is_hashed_but_not_exposed (test_nexus_publication.NexusPublicationTests.test_relative_permission_evidence_is_hashed_but_not_exposed) ... ok\ntest_rendered_page_contains_credits_permissions_and_ai_disclosure (test_nexus_publication.NexusPublicationTests.test_rendered_page_contains_credits_permissions_and_ai_disclosure) ... ok\ntest_stale_policy_review_blocks (test_nexus_publication.NexusPublicationTests.test_stale_policy_review_blocks) ... ok\ntest_unknown_license_requires_manual_acknowledgement (test_nexus_publication.NexusPublicationTests.test_unknown_license_requires_manual_acknowledgement) ... ok\ntest_unmapped_file_blocks_release (test_nexus_publication.NexusPublicationTests.test_unmapped_file_blocks_release) ... ok\ntest_uploader_attestation_cannot_be_mismatched (test_nexus_publication.NexusPublicationTests.test_uploader_attestation_cannot_be_mismatched) ... ok\ntest_valid_release_is_share_ready (test_nexus_publication.NexusPublicationTests.test_valid_release_is_share_ready) ... ok\ntest_line_endings_match_their_declared_attribute (test_packaging.DistributedIntegrityTests.test_line_endings_match_their_declared_attribute) ... ok\ntest_manifest_verifies_against_the_delivered_tree (test_packaging.DistributedIntegrityTests.test_manifest_verifies_against_the_delivered_tree) ... ok\ntest_no_external_build_requirements (test_packaging.PackagingTests.test_no_external_build_requirements) ... ok\ntest_runtime_installation_artifacts_are_never_packaged (test_packaging.PackagingTests.test_runtime_installation_artifacts_are_never_packaged) ... ok\ntest_wheel_determinism_and_native_files (test_packaging.PackagingTests.test_wheel_determinism_and_native_files) ... ok\ntest_batch_files_never_pass_quoted_dp0_as_a_standalone_external_argument (test_powershell_release.PowerShellReleaseTests.test_batch_files_never_pass_quoted_dp0_as_a_standalone_external_argument) ... ok\ntest_no_expandable_string_has_ambiguous_variable_before_colon (test_powershell_release.PowerShellReleaseTests.test_no_expandable_string_has_ambiguous_variable_before_colon) ... ok\ntest_parser_gate_defaults_to_its_own_directory_and_sanitizes_legacy_quote (test_powershell_release.PowerShellReleaseTests.test_parser_gate_defaults_to_its_own_directory_and_sanitizes_legacy_quote) ... ok\ntest_provider_bridge_uses_exact_shared_runtime (test_powershell_release.PowerShellReleaseTests.test_provider_bridge_uses_exact_shared_runtime) ... ok\ntest_python_bootstrap_is_pinned_and_verified (test_powershell_release.PowerShellReleaseTests.test_python_bootstrap_is_pinned_and_verified) ... ok\ntest_runtime_virtualenv_scripts_are_not_release_sources (test_powershell_release.PowerShellReleaseTests.test_runtime_virtualenv_scripts_are_not_release_sources) ... ok\ntest_skill_installer_is_transactional_and_uses_safe_formatting (test_powershell_release.PowerShellReleaseTests.test_skill_installer_is_transactional_and_uses_safe_formatting) ... ok\ntest_start_menu_uses_gate_default_root_and_has_noninteractive_ci_probe (test_powershell_release.PowerShellReleaseTests.test_start_menu_uses_gate_default_root_and_has_noninteractive_ci_probe) ... ok\ntest_windows_ci_exercises_broken_virtualenv_repair (test_powershell_release.PowerShellReleaseTests.test_windows_ci_exercises_broken_virtualenv_repair) ... ok\ntest_python_script_executes_through_current_interpreter (test_process_launch.ProcessLaunchTests.test_python_script_executes_through_current_interpreter) ... ok\ntest_windows_rejects_text_disguised_as_exe_without_launching (test_process_launch.ProcessLaunchTests.test_windows_rejects_text_disguised_as_exe_without_launching) ... ok\ntest_bsarch_adapter_uses_pinned_real_tool_contract_and_reopens (test_toolchain_v42.ToolchainV42Tests.test_bsarch_adapter_uses_pinned_real_tool_contract_and_reopens) ... ok\ntest_bulk_import_is_idempotent_and_reconfigures_existing_vault_entry (test_toolchain_v42.ToolchainV42Tests.test_bulk_import_is_idempotent_and_reconfigures_existing_vault_entry) ... ok\ntest_catalog_disables_public_third_party_binary_bundling (test_toolchain_v42.ToolchainV42Tests.test_catalog_disables_public_third_party_binary_bundling) ... ok\ntest_configure_existing_rejects_gui_as_cli_by_name (test_toolchain_v42.ToolchainV42Tests.test_configure_existing_rejects_gui_as_cli_by_name) ... ok\ntest_nested_bsarch_is_discovered_and_imported_with_pin (test_toolchain_v42.ToolchainV42Tests.test_nested_bsarch_is_discovered_and_imported_with_pin) ... ok\ntest_scan_does_not_execute_candidates (test_toolchain_v42.ToolchainV42Tests.test_scan_does_not_execute_candidates) ... ok\ntest_synthesis_gui_is_never_resolved_as_cli (test_toolchain_v42.ToolchainV42Tests.test_synthesis_gui_is_never_resolved_as_cli) ... ok\ntest_all_built_in_self_tests_are_integrated (test_v41_completion.V41CompletionTests.test_all_built_in_self_tests_are_integrated) ... ok\ntest_papyrus_compile_uses_pinned_python_mock_and_manifest (test_v41_completion.V41CompletionTests.test_papyrus_compile_uses_pinned_python_mock_and_manifest) ... ok\ntest_v41_mcp_surface_is_complete (test_v41_completion.V41CompletionTests.test_v41_mcp_surface_is_complete) ... ok\ntest_capability_registry_has_honest_boundaries (test_v41_engineering.V41EngineeringTests.test_capability_registry_has_honest_boundaries) ... ok\ntest_framework_builder_flags_single_value_skill_without_failing (test_v41_engineering.V41EngineeringTests.test_framework_builder_flags_single_value_skill_without_failing) ... ok\ntest_framework_builder_spid_runtime_proven_shape (test_v41_engineering.V41EngineeringTests.test_framework_builder_spid_runtime_proven_shape) ... ok\ntest_native_binary_audit_distinguishes_dll (test_v41_engineering.V41EngineeringTests.test_native_binary_audit_distinguishes_dll) ... ok\ntest_native_runtime_strategy_conflicts_are_rejected (test_v41_engineering.V41EngineeringTests.test_native_runtime_strategy_conflicts_are_rejected) ... ok\ntest_native_scaffold_uses_one_runtime_strategy_and_staging (test_v41_engineering.V41EngineeringTests.test_native_scaffold_uses_one_runtime_strategy_and_staging) ... ok\ntest_papyrus_case_identity_and_hot_event_are_reviewed (test_v41_engineering.V41EngineeringTests.test_papyrus_case_identity_and_hot_event_are_reviewed) ... ok\ntest_plugin_formkeys_are_required_and_encoded (test_v41_engineering.V41EngineeringTests.test_plugin_formkeys_are_required_and_encoded) ... ok\ntest_skypatcher_typed_build_uses_canonical_path (test_v41_engineering.V41EngineeringTests.test_skypatcher_typed_build_uses_canonical_path) ... ok\ntest_skypatcher_unknown_category_is_warning_not_false_failure (test_v41_engineering.V41EngineeringTests.test_skypatcher_unknown_category_is_warning_not_false_failure) ... ok\ntest_hermes_failure_path_restores_original_configuration (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_failure_path_restores_original_configuration) ... ok\ntest_hermes_registration_is_noninteractive_and_bounded (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_registration_is_noninteractive_and_bounded) ... ok\ntest_hermes_skill_defaults_to_local_app_data (test_windows_provider_bridge.WindowsProviderBridgeTests.test_hermes_skill_defaults_to_local_app_data) ... ok\ntest_kimi_doctor_failure_restores_original_configuration (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_doctor_failure_restores_original_configuration) ... skipped 'Register-MCP integration requires the installed Forge test runtime'\ntest_kimi_registration_preserves_other_servers_and_writes_forge (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_registration_preserves_other_servers_and_writes_forge) ... skipped 'Register-MCP integration requires the installed Forge test runtime'\ntest_kimi_rejects_array_shaped_server_map_without_overwriting (test_windows_provider_bridge.WindowsProviderBridgeTests.test_kimi_rejects_array_shaped_server_map_without_overwriting) ... skipped 'Register-MCP integration requires the installed Forge test runtime'\n\n----------------------------------------------------------------------\nRan 159 tests in 2.890s\n\nOK (skipped=4)\n", "stdout": "" } }, @@ -275,25 +275,24 @@ "hardcoded_in_ci": [], "result": "PASS", "sources": { - "CURRENT.txt": "5.2.1", - "README.md": "5.2.1", - "START-HERE.bat": "5.2.1", - "VERSION.txt": "5.2.1", - "integrations/skyrim-forge/SKILL.md (series)": "5.2", - "pyproject.toml": "5.2.1", - "skyrim_forge/version.py": "5.2.1", - "writer/native-go/main.go": "5.2.1" + "CURRENT.txt": "6.0.0", + "README.md": "6.0.0", + "START-HERE.bat": "6.0.0", + "VERSION.txt": "6.0.0", + "integrations/skyrim-forge/SKILL.md (series)": "6.0", + "pyproject.toml": "6.0.0", + "skyrim_forge/version.py": "6.0.0", + "writer/native-go/main.go": "6.0.0" }, - "version": "5.2.1" + "version": "6.0.0" } }, "errors": [], "product": "Skyrim Forge", "result": "PASS", "scope": "full", - "version": "5.2.1", + "version": "6.0.0", "warnings": [ - "PowerShell syntax is statically screened here; Windows CI executes the shipped parser gate, the exact START-HERE batch startup path, and every skill-provider installation", - "PyYAML unavailable; workflow YAML syntax parsing skipped" + "PowerShell syntax is statically screened here; Windows CI executes the shipped parser gate, the exact START-HERE batch startup path, and every skill-provider installation" ] } diff --git a/VERSION.txt b/VERSION.txt index 22b8455..c178f72 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,3 +1,3 @@ -Skyrim Forge 5.2.1 -Parent: Skyrim Forge 5.2.0 +Skyrim Forge 6.0.0 +Parent: Skyrim Forge 5.2.1 Status: validated release diff --git a/integrations/skyrim-forge/SKILL.md b/integrations/skyrim-forge/SKILL.md index 6f9cd99..c7eb4a1 100644 --- a/integrations/skyrim-forge/SKILL.md +++ b/integrations/skyrim-forge/SKILL.md @@ -1,9 +1,9 @@ --- name: skyrim-forge -description: Use Skyrim Forge 5.2 as the primary typed automation broker for Skyrim mod development and validation. +description: Use when Skyrim mod development or validation can benefit from the bundle-managed Skyrim Forge typed automation broker. --- -# Skyrim Forge 5.2 +# Skyrim Forge 6.0 ## Resolve this installation @@ -19,16 +19,45 @@ If the descriptor is unavailable, resolve Forge in this order: 3. an explicit path supplied by the user. Never assume a drive letter or reconstruct the application elsewhere. -Never treat `%USERPROFILE%\Documents\SkyrimForge` or -`Documents\Skyrim Forge` as the live product. The live install is -`SKYRIM_FORGE_ROOT`, a versioned `Skyrim-Forge-` folder under the -user's Skyrim tools directory. +Never treat `Documents\SkyrimForge` or `Documents\Skyrim Forge` as the live +product. The bundle-managed live install is the versionless `Skyrim-Forge` +directory named by `SKYRIM_FORGE_ROOT`; upgrades replace the application source +in place while preserving the managed workspace/runtime state. Do not reconstruct +a version-stamped install path. + +Forge 6 ships in the same repository as this bundle, so there is no cross-product +version handshake. Run `forge doctor` (or the exact descriptor CLI argv plus +`doctor`) and require `result: PASS` and `read_only_ready: true` before major +work. Claude Code 2026-07-28 still requires `tools/call` to include +`resultType: "complete"`. Do not add Forge as a new Grok MCP server when +Grok already sits on the 8-running-server cliff (7 configured, or 6 while +`mcp-search` still loads). + +## Venv health (Windows) — the provider-runtime trap + +Forge's MCP server runs from `/.venv/Scripts/python.exe -m skyrim_forge mcp`. +If that venv was created from a **provider runtime cache** python (e.g. +`~/.cache/codex-runtimes/.../python.exe`), it dies the moment the runtime cache is +deleted or that provider is uninstalled. Symptom: MCP clients (Grok, Claude Code, +Codex) hang at startup waiting on a server that cannot boot; the venv python +prints `No Python at ''`. + +Check: `cat /.venv/pyvenv.cfg` — if `home =` points into a cache dir, repair: -Require Forge 5.1.5+ for Claude Code 2026-07-28 (`tools/call` must include -`resultType: "complete"`). Do not add Forge as a new Grok MCP server when -Grok already has 7 configured servers, or 6 while `mcp-search` still loads. +```text +py -3.12 -m venv "/.venv" +"/.venv/Scripts/python.exe" -m pip install "" +``` + +Forge has zero external dependencies and a local build backend, so this is +fast and offline. Verify the MCP server answers (must return JSON, not hang): + +```text +printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}\n' | "/.venv/Scripts/python.exe" -m skyrim_forge mcp +``` -Run `forge doctor` before major Skyrim work. +Then restart the MCP client. Never build the venv from a provider runtime +python — use a stable interpreter (system Python, `py -3.x`, or `uv`). Use Forge inspection and typed jobs before inventing one-off scripts. Never launch xEdit, Creation Kit, LOOT, or Wrye Bash and leave the user to click. Use an Automation Fabric job or report that the required adapter is unavailable. @@ -99,3 +128,15 @@ Never call a mod Nexus-compliant merely because its files compile or lint. The r - Never substitute a GUI for a CLI. `Synthesis.exe` is not `Synthesis.Bethesda.CLI.exe`. - Use BSArch for `.bsa`/`.ba2` work when the `archive.bsa.*` capability resolves. Use the official Bethesda Papyrus compiler for publication builds when `papyrus.compile.official` resolves. Use Champollion only for recovery/analysis, never as proof of original source ownership. - When no eligible real tool resolves, stop and report the missing adapter instead of generating an unverified replacement. + +--- + +## Portable discovery + +1. Read `INSTALLATION.json` beside this skill when present (per-machine; do not ship secrets). +2. Else `$env:SKYRIM_FORGE_ROOT`. +3. Else `forge` on PATH. +4. Else user path. +5. Else, when working from the bundle, run `TOOLS/Install-SkyrimForge.ps1`; do not invent a manual extraction path. + +After a bundle-managed install, missing or unhealthy Forge is a failed installation state, not a successful optional skip. Outside the bundle, report Forge as unavailable rather than inventing paths. diff --git a/pyproject.toml b/pyproject.toml index 272dc55..865131e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ backend-path = ["."] [project] name = "skyrim-forge" -version = "5.2.1" +version = "6.0.0" description = "Safety-first Skyrim mod engineering, automation fabric, and Nexus publication gate" readme = "README.md" requires-python = ">=3.11" diff --git a/scripts/rebuild_native_helpers.py b/scripts/rebuild_native_helpers.py index 58d7816..84df81c 100644 --- a/scripts/rebuild_native_helpers.py +++ b/scripts/rebuild_native_helpers.py @@ -33,7 +33,14 @@ def pinned_toolchain() -> str: GOTOOLCHAIN makes the local `go` fetch and use that exact version, so the result matches CI regardless of what is installed. """ - workflow = ROOT / ".github" / "workflows" / "ci.yml" + workflow = None + for parent in (ROOT, *ROOT.parents): + candidate = parent / ".github" / "workflows" / "ci.yml" + if candidate.is_file(): + workflow = candidate + break + if workflow is None: + raise SystemExit("no repository-root .github/workflows/ci.yml found") versions = re.findall(r'go-version:\s*"([0-9]+\.[0-9]+(?:\.[0-9]+)?)"', workflow.read_text(encoding="utf-8")) if not versions: diff --git a/scripts/validate_repository.py b/scripts/validate_repository.py index 9057ee7..d79d9e0 100644 --- a/scripts/validate_repository.py +++ b/scripts/validate_repository.py @@ -49,8 +49,13 @@ def _pinned_go_toolchain() -> str: ci.yml is the single source, so this reader and the one in scripts/rebuild_native_helpers.py cannot drift apart. """ - workflow = ROOT / ".github" / "workflows" / "ci.yml" - if not workflow.exists(): + workflow = None + for parent in (ROOT, *ROOT.parents): + candidate = parent / ".github" / "workflows" / "ci.yml" + if candidate.is_file(): + workflow = candidate + break + if workflow is None: return "" versions = re.findall(r'go-version:\s*"([0-9]+\.[0-9]+(?:\.[0-9]+)?)"', workflow.read_text(encoding="utf-8")) @@ -367,7 +372,14 @@ def validate_mcp(errors: list[str]) -> dict[str, Any]: def _powershell_expandable_strings(text: str) -> list[tuple[int, str]]: strings: list[tuple[int, str]] = [] for line_number, line in enumerate(text.splitlines(), 1): - for match in re.finditer(r'"(?:[^"`\r\n]|`.)*"', line): + # Disjoint alternatives. The escape branch starts with a backtick, so + # the ordinary branch excludes it -- a backtick also satisfies + # [^"\r\n], and that overlap is the py/redos CodeQL flagged. Restoring + # the parenthesis the autofix dropped made it compile and left the + # blowup where it was (4.2 s against 0 ms on an unterminated string + # with 26 escapes) -- and, by putting the ordinary branch first, made + # an escaped quote `" end the string instead of continuing it. + for match in re.finditer(r'"(?:`[^\r\n]|[^"`\r\n])*"', line): strings.append((line_number, match.group(0))) for match in re.finditer(r'@"\r?\n(.*?)\r?\n"@', text, flags=re.S): line_number = text.count("\n", 0, match.start()) + 1 @@ -389,7 +401,9 @@ def validate_powershell(errors: list[str], warnings: list[str]) -> dict[str, Any if bad: findings.append(f"ambiguous variable followed by colon at {path.name}:{line_number}: {bad.group(0)}") # Simple delimiter scan after removing strings/comments is not a parser, but catches accidental truncation. - scrub=re.sub(r"(?m)#.*$|'(?:''|[^'])*'|\"(?:[^\"`]|`.)*\"","",script_text) + # Same disjointness rule; [\s\S] rather than . so a backtick before a + # newline -- PowerShell's line continuation -- is still consumed. + scrub=re.sub(r"(?m)#.*$|'(?:''|[^'])*'|\"(?:`[\s\S]|[^\"`])*\"","",script_text) for left,right in (("{","}"),("(",")")): if scrub.count(left)!=scrub.count(right): findings.append(f"unbalanced {left}{right}: {path.name}") quoted_dp0 = re.compile(r'"%~dp0"(?=\s|$)', re.I) @@ -487,10 +501,26 @@ def capture(rel: str, pattern: str) -> None: sources["integrations/skyrim-forge/SKILL.md (series)"] = series if skill_versions and not stale_skill else (stale_skill[0] if stale_skill else None) # The workflow must derive the expected native string rather than hardcode # it; a literal here is exactly what went stale before. - workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8") - hardcoded = re.findall(r"SkyrimForge\.Native [0-9]+\.[0-9]+\.[0-9]+ go", workflow) - if hardcoded: - errors.append(f"ci.yml hardcodes a native version string: {sorted(set(hardcoded))}") + # + # Forge ships inside the Ultimate AI Starter Bundle monorepo, so the + # workflow that runs these jobs lives at the CHECKOUT root and not in this + # subtree -- GitHub only reads .github/workflows from the repository root. + # Walk up to find it, and check every workflow it holds rather than one + # remembered filename. + workflow_dir = next( + (parent / ".github" / "workflows" + for parent in (ROOT, *ROOT.parents) + if (parent / ".github" / "workflows").is_dir()), + None, + ) + hardcoded: list[str] = [] + # An extracted install carries no .github at all. There is no workflow to be + # wrong about, so absence is not a finding. + for path in sorted(workflow_dir.glob("*.yml")) if workflow_dir else []: + found = re.findall(r"SkyrimForge\.Native [0-9]+\.[0-9]+\.[0-9]+ go", path.read_text(encoding="utf-8")) + if found: + errors.append(f"{path.name} hardcodes a native version string: {sorted(set(found))}") + hardcoded += found # The archive builder names the release directory and must not restate it. archive_builder = (ROOT / "scripts" / "build_release_archive.py").read_text(encoding="utf-8") literal = re.findall(r'^VERSION\s*=\s*"[0-9]+\.[0-9]+\.[0-9]+"', archive_builder, re.M) diff --git a/skyrim_forge/bin/linux-x64/SkyrimForge.Native b/skyrim_forge/bin/linux-x64/SkyrimForge.Native index 7c5246cf0e6da37b9699d1be34f9280428aa3b4e..396f8954f27a45de17aa0197a1ada5a60a595ef8 100755 GIT binary patch delta 113 zcmZo)SkS((prM7ag{g(Pg{6hHg{_6Xg`v@2fcRSO1 vKIIOmn3g$zyOy~C5DNma5D*Iku?P^00DZe9$qOLi delta 113 zcmZo)SkS((prM7ag{g(Pg{6hHg{_6Xg`v@2fcRSO1 vKIIOmn3g$zyOy~C5DNma5D*Iku?P^00DZe9$`2sr diff --git a/skyrim_forge/bin/win-x64/SkyrimForge.Native.exe b/skyrim_forge/bin/win-x64/SkyrimForge.Native.exe index 5deab6f8b3ef8321950b5776eba525b16682e19b..08cad572d9c13cb1dd034d6bf17c5c4f73093535 100644 GIT binary patch delta 123 zcmZozQ{DhXEsQNpEzB(}EvzkUE$l5EEu1Y}E!-_UTX>aqSaqSxxnf^bFfIba{c8Z@Y#r zf7WcM*z^ tuple[int, int, int]: - parts = value.strip().lstrip("vV").split(".") - if len(parts) != 3 or any(not part.isdigit() for part in parts): - raise ValueError(f"Expected semantic version X.Y.Z, got {value!r}") - return tuple(int(part) for part in parts) # type: ignore[return-value] - - -def evaluate_bundle_contract(bundle_version: str) -> dict[str, Any]: - """Return the machine-readable compatibility handshake used by bundle 7.8+.""" - try: - parsed = _version_tuple(bundle_version) - except ValueError as exc: - return { - "product": "Skyrim Forge", - "forge_version": VERSION, - "bundle_version": bundle_version, - "contract_version": CONTRACT_VERSION, - "result": "FAIL", - "reason": str(exc), - "capabilities": {}, - } - - capabilities = { - "mcp_stdio": True, - "provider_registration": True, - "doctor": True, - "self_test": True, - "bundle_contract": True, - } - if parsed < MIN_BUNDLE: - result = "FAIL" - reason = "Skyrim Forge 5.2 requires Ultimate AI Starter Bundle >= 7.8.0" - elif parsed >= MAX_BUNDLE_EXCLUSIVE: - result = "FAIL" - reason = "Skyrim Forge 5.2 contract supports Ultimate AI Starter Bundle >= 7.8.0 and < 8.0.0" - else: - result = "PASS" - reason = "Compatible" - return { - "product": "Skyrim Forge", - "forge_version": VERSION, - "bundle_version": bundle_version, - "contract_version": CONTRACT_VERSION, - "supported_bundle_range": ">=7.8.0,<8.0.0", - "result": result, - "reason": reason, - "capabilities": capabilities, - } diff --git a/skyrim_forge/cli.py b/skyrim_forge/cli.py index 9f13fc7..9420519 100644 --- a/skyrim_forge/cli.py +++ b/skyrim_forge/cli.py @@ -19,7 +19,6 @@ def parser() -> argparse.ArgumentParser: sub.add_parser("version") sub.add_parser("doctor") sub.add_parser("self-test") - bc = sub.add_parser("bundle-contract"); bc.add_argument("--bundle-version", required=True) sub.add_parser("config-show") setp = sub.add_parser("config-set"); setp.add_argument("key"); setp.add_argument("value") sub.add_parser("discover-tools") @@ -92,11 +91,6 @@ def main(argv: list[str] | None = None) -> int: value = run_all() print(dumps(value)) return _exit_code(value) - if args.command == "bundle-contract": - from .bundle_contract import evaluate_bundle_contract - value = evaluate_bundle_contract(args.bundle_version) - print(dumps(value)) - return _exit_code(value) if args.command == "mcp": from .mcp_server import serve serve(args.config) diff --git a/skyrim_forge/version.py b/skyrim_forge/version.py index 91ab71e..d8fc426 100644 --- a/skyrim_forge/version.py +++ b/skyrim_forge/version.py @@ -1 +1 @@ -VERSION = "5.2.1" +VERSION = "6.0.0" diff --git a/tests/_workflows.py b/tests/_workflows.py new file mode 100644 index 0000000..7d05305 --- /dev/null +++ b/tests/_workflows.py @@ -0,0 +1,27 @@ +"""Locate the CI workflows that actually build and test this subtree. + +Forge is developed inside the Ultimate AI Starter Bundle repository, and GitHub +reads `.github/workflows` only from a repository ROOT. This subtree therefore +carries no workflows of its own -- the jobs that run it live at the checkout +root. Tests that assert something about CI have to look there, or they assert it +about a file that no longer exists and pass by never running. +""" + +from __future__ import annotations + +from pathlib import Path + +FORGE_ROOT = Path(__file__).resolve().parents[1] + + +def workflows_dir() -> Path | None: + """The nearest enclosing `.github/workflows`, or None outside a checkout. + + An extracted install has no `.github` anywhere above it. That is not a + finding -- there is no CI there to be wrong about. + """ + for parent in (FORGE_ROOT, *FORGE_ROOT.parents): + candidate = parent / ".github" / "workflows" + if candidate.is_dir(): + return candidate + return None diff --git a/tests/test_bundle_contract_v520.py b/tests/test_bundle_contract_v520.py deleted file mode 100644 index 857b7b5..0000000 --- a/tests/test_bundle_contract_v520.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -import json -import subprocess -import sys -import unittest - -from skyrim_forge.bundle_contract import evaluate_bundle_contract -from skyrim_forge.version import VERSION - - -class BundleContractTests(unittest.TestCase): - def test_product_version_is_the_5_2_contract_series(self): - # Pin the SERIES, not the patch. The contract range in - # bundle_contract.py is what 5.2 promises a bundle; a patch bump does - # not change it, and a test that hardcodes 5.2.0 just turns every - # patch release into a test edit -- the same restated-literal drift - # the version gate exists to catch. - self.assertEqual(VERSION.split(".")[:2], ["5", "2"]) - - def test_ultimate_bundle_7_8_0_is_compatible(self): - report = evaluate_bundle_contract("7.8.0") - self.assertEqual(report["result"], "PASS", report) - self.assertEqual(report["contract_version"], 1) - self.assertEqual(report["forge_version"], VERSION) - self.assertEqual(report["bundle_version"], "7.8.0") - self.assertTrue(report["capabilities"]["mcp_stdio"]) - self.assertTrue(report["capabilities"]["provider_registration"]) - - def test_pre_7_8_bundle_is_rejected(self): - report = evaluate_bundle_contract("7.7.15") - self.assertEqual(report["result"], "FAIL") - self.assertIn("requires Ultimate AI Starter Bundle >= 7.8.0", report["reason"]) - - def test_next_bundle_major_requires_new_contract(self): - report = evaluate_bundle_contract("8.0.0") - self.assertEqual(report["result"], "FAIL") - self.assertIn("< 8.0.0", report["reason"]) - - def test_cli_contract_is_machine_readable_and_exit_code_tracks_result(self): - ok = subprocess.run( - [sys.executable, "-m", "skyrim_forge", "bundle-contract", "--bundle-version", "7.8.0"], - text=True, - capture_output=True, - ) - self.assertEqual(ok.returncode, 0, ok.stdout + ok.stderr) - self.assertEqual(json.loads(ok.stdout)["result"], "PASS") - - bad = subprocess.run( - [sys.executable, "-m", "skyrim_forge", "bundle-contract", "--bundle-version", "7.7.15"], - text=True, - capture_output=True, - ) - self.assertEqual(bad.returncode, 2, bad.stdout + bad.stderr) - self.assertEqual(json.loads(bad.stdout)["result"], "FAIL") - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_ci_validation.py b/tests/test_ci_validation.py index 4c6a917..8359d26 100644 --- a/tests/test_ci_validation.py +++ b/tests/test_ci_validation.py @@ -5,6 +5,8 @@ import unittest from pathlib import Path +from _workflows import workflows_dir + class CIValidationScopeTests(unittest.TestCase): @classmethod @@ -31,6 +33,25 @@ def test_unknown_scope_is_rejected(self): with self.assertRaises(ValueError): self.module.validation_checks("unknown") + def test_embedded_validator_resolves_root_go_toolchain(self): + self.assertEqual( + self.module.GO_TOOLCHAIN, + "go1.23.2", + "embedded Forge validator lost the repository-root Go pin", + ) + + def test_embedded_rebuild_helper_resolves_root_go_toolchain(self): + script = Path(__file__).resolve().parents[1] / "scripts" / "rebuild_native_helpers.py" + spec = importlib.util.spec_from_file_location("forge_rebuild_native_helpers", script) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + self.assertEqual( + module.TOOLCHAIN, + "go1.23.2", + "embedded Forge rebuild helper lost the repository-root Go pin", + ) + class WorkflowPinningTests(unittest.TestCase): """CodeQL's init and analyze actions must always run the same release. @@ -41,9 +62,11 @@ class WorkflowPinningTests(unittest.TestCase): version '4.36.0'". Four pull requests sat blocked behind it. """ - WORKFLOWS = Path(__file__).resolve().parents[1] / ".github" / "workflows" + WORKFLOWS = workflows_dir() def test_codeql_action_versions_are_pinned_together(self): + if self.WORKFLOWS is None: + self.skipTest("no workflows above this subtree") pinned = set() for workflow in self.WORKFLOWS.glob("*.yml"): for match in re.finditer(r"github/codeql-action/\w+@(\S+)", workflow.read_text(encoding="utf-8")): @@ -53,14 +76,22 @@ def test_codeql_action_versions_are_pinned_together(self): self.assertEqual(len(pinned), 1, f"codeql-action steps are pinned to different revisions: {sorted(pinned)}") def test_dependabot_groups_the_codeql_pair(self): - config = Path(__file__).resolve().parents[1] / ".github" / "dependabot.yml" + if self.WORKFLOWS is None: + self.skipTest("no workflows above this subtree") + config = self.WORKFLOWS.parent / "dependabot.yml" if not config.exists(): self.skipTest("no dependabot configuration present") self.assertIn("github/codeql-action", config.read_text(encoding="utf-8"), "dependabot must group codeql-action so the pair cannot be split across pull requests") def test_release_publish_is_idempotent(self): - workflow = (self.WORKFLOWS / "release.yml").read_text(encoding="utf-8") + # Forge stopped publishing releases of its own when it moved into the + # bundle repository. If a release workflow ever appears at the checkout + # root, re-running it over an existing tag must still not fail. + release = self.WORKFLOWS / "release.yml" if self.WORKFLOWS else None + if release is None or not release.is_file(): + self.skipTest("no release workflow present") + workflow = release.read_text(encoding="utf-8") self.assertIn('gh release view "$GITHUB_REF_NAME"', workflow) self.assertIn('gh release upload "$GITHUB_REF_NAME" dist/* --clobber', workflow) self.assertIn('gh release create "$GITHUB_REF_NAME" dist/* --verify-tag --generate-notes', workflow) diff --git a/tests/test_powershell_release.py b/tests/test_powershell_release.py index 984086a..b22a5ab 100644 --- a/tests/test_powershell_release.py +++ b/tests/test_powershell_release.py @@ -7,6 +7,8 @@ ROOT = Path(__file__).resolve().parents[1] +from _workflows import workflows_dir + def owned_powershell_scripts(root: Path) -> list[Path]: scripts = [*root.glob('*.ps1')] @@ -26,7 +28,8 @@ def test_no_expandable_string_has_ambiguous_variable_before_colon(self): for path in owned_powershell_scripts(ROOT): text = path.read_text(encoding='utf-8-sig') for line_number, line in enumerate(text.splitlines(), 1): - for match in re.finditer(r'"(?:[^"`\r\n]|`.)*"', line): + # Disjoint alternatives; see validate_repository.py. + for match in re.finditer(r'"(?:`[^\r\n]|[^"`\r\n])*"', line): invalid = bad.search(match.group(0)) if invalid: findings.append(f'{path.relative_to(ROOT)}:{line_number}:{invalid.group(0)}') @@ -100,7 +103,12 @@ def test_python_bootstrap_is_pinned_and_verified(self): self.assertIn("'tools.ui_worker.worker_sha256'", installer) def test_windows_ci_exercises_broken_virtualenv_repair(self): - workflow = (ROOT / '.github' / 'workflows' / 'ci.yml').read_text(encoding='utf-8') + # Not skippable. A Forge subtree with no CI above it would mean the + # bundle stopped running the Windows installer job, and this repair + # path is the one that has actually shipped broken. + workflows = workflows_dir() + self.assertIsNotNone(workflows, 'no .github/workflows above the Forge subtree') + workflow = (workflows / 'ci.yml').read_text(encoding='utf-8') self.assertIn("[IO.File]::WriteAllBytes((Join-Path $PWD '.venv/Scripts/python.exe')", workflow) self.assertIn('Broken virtual environment was not repaired.', workflow) @@ -123,9 +131,17 @@ def test_provider_bridge_uses_exact_shared_runtime(self): self.assertIn("'Kimi'", register) self.assertIn("'Hermes'", register) self.assertIn("Join-Path $KimiHome 'mcp.json'", register) - self.assertIn('mcp test skyrim-forge', register) - self.assertIn("'Y' | & $Executable mcp add skyrim-forge", register) - self.assertIn("$HermesTestOutput -notmatch", register) + self.assertNotIn("Start-Process -FilePath $Executable -ArgumentList @('mcp','test','skyrim-forge')", register) + self.assertNotIn("'Y' | & $Executable mcp add skyrim-forge", register) + self.assertIn('from hermes_cli.config import load_config, save_config', register) + self.assertIn('from hermes_cli.mcp_config import _probe_single_server', register) + self.assertIn("Start-Process -FilePath $HermesPython", register) + self.assertIn('\"connect_timeout\": 30', register) + self.assertIn('hard limit 45s', register) + self.assertIn('$HermesProcess.WaitForExit(', register) + self.assertIn('$HermesProcess.WaitForExit()', register) + self.assertNotIn('$HermesProcess.HasExited', register) + self.assertIn("$HermesProbe.connected", register) self.assertIn("'hermes-agent\\venv\\Scripts\\hermes.exe'", register) self.assertNotIn('$Name?', register) self.assertIn('${Name}?', register) diff --git a/tests/test_windows_provider_bridge.py b/tests/test_windows_provider_bridge.py index be047dd..a4cb7ad 100644 --- a/tests/test_windows_provider_bridge.py +++ b/tests/test_windows_provider_bridge.py @@ -125,71 +125,25 @@ def test_kimi_rejects_array_shaped_server_map_without_overwriting(self): provider = json.loads(report.read_text(encoding="utf-8-sig"))["providers"][0] self.assertEqual((provider["mode"], provider["status"]), ("mcp", "FAILED")) - def test_hermes_registration_uses_supported_cli_and_tests_connection(self): - python = self.require_installed_test_runtime() - with tempfile.TemporaryDirectory() as td: - temp = Path(td) - fake_bin = temp / "bin" - fake_bin.mkdir() - call_log = temp / "hermes-calls.txt" - (fake_bin / "hermes.cmd").write_text( - '@echo %*>>"%FAKE_HERMES_LOG%"\n' - '@if "%1 %2"=="mcp test" @echo Connected! Found 52 tools\n' - '@exit /b 0\n', - encoding="ascii", - ) - report = temp / "report.json" - env = os.environ.copy() - env.update({ - "HERMES_HOME": str(temp / "hermes-home"), - "FAKE_HERMES_LOG": str(call_log), - "PATH": str(fake_bin) + os.pathsep + env["PATH"], - }) - result = self.run_script( - ROOT / "Register-MCP.ps1", "-Provider", "Hermes", "-Yes", "-ReportPath", str(report), env=env - ) - self.assertEqual(result.returncode, 0, result.stdout + result.stderr) - # `hermes.cmd` logs %*, the raw child command line. PowerShell quotes - # any argument containing a space, so an install under a path like - # "S:\Apps\Skyrim Tools\..." is logged as --command "S:\Apps\...". - # That quoting is required -- without it hermes would receive - # `--command S:\Apps\Skyrim` and `Tools\...` as two arguments -- so - # compare with quotes stripped rather than asserting the bare form, - # which only ever held for space-free install paths. - calls = [line.replace('"', "") for line in - call_log.read_text(encoding="utf-8").splitlines()] - self.assertIn(f"mcp add skyrim-forge --command {python} --args -m skyrim_forge mcp", calls) - self.assertIn("mcp test skyrim-forge", calls) - provider = json.loads(report.read_text(encoding="utf-8-sig"))["providers"][0] - self.assertEqual((provider["mode"], provider["status"]), ("mcp", "READY")) + def test_hermes_registration_is_noninteractive_and_bounded(self): + register = (ROOT / "Register-MCP.ps1").read_text(encoding="utf-8-sig") + self.assertIn("from hermes_cli.config import load_config, save_config", register) + self.assertIn('"connect_timeout": 30', register) + self.assertNotIn("'Y' | & $Executable mcp add skyrim-forge", register) + # The probe runs Hermes' own interpreter against a helper script rather + # than hermes.exe: full CLI startup registers shell hooks, and Hermes + # prompts on first use of an unseen hook when stdin is a TTY, which hung + # an unattended bundle install. + self.assertIn("Start-Process -FilePath $HermesPython -ArgumentList @($HermesConfigHelper)", register) + self.assertIn("Hermes direct MCP probe timed out after 45 seconds.", register) + self.assertIn("direct Forge MCP probe still running", register) - def test_hermes_test_failure_restores_original_configuration(self): - self.require_installed_test_runtime() - with tempfile.TemporaryDirectory() as td: - temp = Path(td) - hermes_home = temp / "hermes-home" - hermes_home.mkdir() - config = hermes_home / "config.yaml" - original = "mcp_servers:\n keep:\n command: keep.exe\n" - config.write_text(original, encoding="utf-8") - fake_bin = temp / "bin" - fake_bin.mkdir() - (fake_bin / "hermes.cmd").write_text( - '@if "%1 %2"=="mcp add" echo modified>"%HERMES_HOME%\\config.yaml"\n' - '@if "%1 %2"=="mcp test" exit /b 9\n' - '@exit /b 0\n', - encoding="ascii", - ) - report = temp / "report.json" - env = os.environ.copy() - env.update({"HERMES_HOME": str(hermes_home), "PATH": str(fake_bin) + os.pathsep + env["PATH"]}) - result = self.run_script( - ROOT / "Register-MCP.ps1", "-Provider", "Hermes", "-Yes", "-ReportPath", str(report), env=env - ) - self.assertNotEqual(result.returncode, 0) - self.assertEqual(config.read_text(encoding="utf-8"), original) - provider = json.loads(report.read_text(encoding="utf-8-sig"))["providers"][0] - self.assertEqual((provider["mode"], provider["status"]), ("mcp", "FAILED")) + def test_hermes_failure_path_restores_original_configuration(self): + register = (ROOT / "Register-MCP.ps1").read_text(encoding="utf-8-sig") + self.assertIn("$HermesOriginal = if (Test-Path -LiteralPath $HermesConfigPath", register) + self.assertIn("[IO.File]::WriteAllBytes($HermesConfigPath, $HermesOriginal)", register) + self.assertIn("Remove-Item -LiteralPath $HermesConfigPath -Force", register) + self.assertIn("throw", register) if __name__ == "__main__": diff --git a/writer/native-go/main.go b/writer/native-go/main.go index e03d691..3fea9a3 100644 --- a/writer/native-go/main.go +++ b/writer/native-go/main.go @@ -11,7 +11,7 @@ import ( "path/filepath" ) -const version = "5.2.1" +const version = "6.0.0" type receipt struct { Result string `json:"result"` diff --git a/writer/published/linux-x64/SkyrimForge.Native b/writer/published/linux-x64/SkyrimForge.Native index 7c5246cf0e6da37b9699d1be34f9280428aa3b4e..396f8954f27a45de17aa0197a1ada5a60a595ef8 100755 GIT binary patch delta 113 zcmZo)SkS((prM7ag{g(Pg{6hHg{_6Xg`v@2fcRSO1 vKIIOmn3g$zyOy~C5DNma5D*Iku?P^00DZe9$qOLi delta 113 zcmZo)SkS((prM7ag{g(Pg{6hHg{_6Xg`v@2fcRSO1 vKIIOmn3g$zyOy~C5DNma5D*Iku?P^00DZe9$`2sr diff --git a/writer/published/win-x64/SkyrimForge.Native.exe b/writer/published/win-x64/SkyrimForge.Native.exe index 5deab6f8b3ef8321950b5776eba525b16682e19b..08cad572d9c13cb1dd034d6bf17c5c4f73093535 100644 GIT binary patch delta 123 zcmZozQ{DhXEsQNpEzB(}EvzkUE$l5EEu1Y}E!-_UTX>aqSaqSxxnf^bFfIba{c8Z@Y#r zf7WcM*z^