mirror of
https://github.com/motajs/template.git
synced 2026-09-12 17:58:51 +08:00
wip: pause Phase 3 latest correction batch
This commit is contained in:
parent
f6a376137e
commit
81c3653b90
@ -1,52 +1,45 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"timestamp": "2026-09-11T09:34:07.509Z",
|
||||
"timestamp": "2026-09-11T14:33:17.222Z",
|
||||
"phase": "03",
|
||||
"phase_name": "data-completion",
|
||||
"phase_dir": ".planning/phases/03-data-completion",
|
||||
"plan": 17,
|
||||
"task": 0,
|
||||
"total_tasks": 3,
|
||||
"task": 2,
|
||||
"total_tasks": 2,
|
||||
"status": "paused",
|
||||
"completed_tasks": [],
|
||||
"completed_tasks": [
|
||||
{"id": 1, "name": "Replay completion and command ownership correction", "status": "done", "commit": "df7d1e1"},
|
||||
{"id": 2, "name": "Replay registry regression coverage", "status": "done", "commit": "4bd6a46"}
|
||||
],
|
||||
"remaining_tasks": [
|
||||
{"id": 17, "name": "Replay command and movement completion correction", "status": "plan_review_pending"},
|
||||
{"id": 18, "name": "Class-based event registrations and hero ownership", "status": "plan_review_pending"},
|
||||
{"id": 19, "name": "Multiline JSDoc and style correction", "status": "plan_review_pending"}
|
||||
{"id": 18, "name": "Class-based event registrations and hero ownership", "status": "not_started"},
|
||||
{"id": 19, "name": "Multiline JSDoc and style correction", "status": "not_started"}
|
||||
],
|
||||
"blockers": [
|
||||
{
|
||||
"description": "User requested a session pause before executing the newly revised correction plans.",
|
||||
"description": "User requested a pause before executing Plans 03-18 and 03-19.",
|
||||
"type": "human_action",
|
||||
"workaround": "Resume with /gsd-resume-work."
|
||||
},
|
||||
{
|
||||
"description": "Plan 03-17 scope guard is incompatible with Windows PowerShell 5.1; its equivalent compatibility check passed.",
|
||||
"type": "technical",
|
||||
"workaround": "Review or replace the scope guard before relying on it in a future execution."
|
||||
}
|
||||
],
|
||||
"async_jobs": [],
|
||||
"human_actions_pending": [],
|
||||
"decisions": [
|
||||
{
|
||||
"decision": "Replay commands must not call shouldReplay; the user will place decorators on lower-level state mutation methods.",
|
||||
"rationale": "Replay is a verifier and should not mark its own command entry points.",
|
||||
"phase": "03"
|
||||
},
|
||||
{
|
||||
"decision": "Movement and pathfinding replay steps must await controller completion before the next step.",
|
||||
"rationale": "A later route step must not overlap an unfinished movement action.",
|
||||
"phase": "03"
|
||||
},
|
||||
{
|
||||
"decision": "Directional replay commands share one parameterized class; event registrations use explicit classes; eventTouchFront belongs to hero events.",
|
||||
"rationale": "Avoid duplicated replay classes and keep event ownership aligned with domain responsibility.",
|
||||
"phase": "03"
|
||||
}
|
||||
{"decision": "Replay commands contain no shouldReplay calls; decorators remain user-owned at lower-level state mutation methods.", "rationale": "Replay verifies state changes and must not mark its own command entry points.", "phase": "03"},
|
||||
{"decision": "Movement and pathfinding replay commands await controller completion.", "rationale": "The next replay step must not overlap an unfinished action.", "phase": "03"},
|
||||
{"decision": "Directional replay commands use one parameterized class.", "rationale": "The four directional commands share identical behavior.", "phase": "03"}
|
||||
],
|
||||
"uncommitted_files": [
|
||||
" M .planning/STATE.md",
|
||||
" M .planning/phases/03-data-completion/03-17-PLAN.md",
|
||||
" M .planning/phases/03-data-completion/03-18-PLAN.md",
|
||||
" M .planning/phases/03-data-completion/03-19-PLAN.md",
|
||||
" M .planning/phases/03-data-completion/03-CONTEXT.md",
|
||||
" M .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md",
|
||||
" M .planning/phases/03-data-completion/03-VERIFICATION.md",
|
||||
" M packages-user/data-common/src/save/index.ts",
|
||||
" M packages-user/data-state/src/core.ts",
|
||||
" M packages-user/data-state/test/coreNode.test.ts",
|
||||
@ -54,6 +47,6 @@
|
||||
" M packages-user/data-state/test/nodeTracer.test.ts",
|
||||
" M packages-user/data-system/src/types.ts"
|
||||
],
|
||||
"next_action": "Run the plan checker against revised Plans 03-17 through 03-19, then execute Plan 03-17 only after it passes.",
|
||||
"context_notes": "No correction code from the new batch has executed. Preserve unrelated existing working-tree changes. Do not modify user-owned @shouldReplay() placement on lower-level state methods. The seven legacy/client compatibility cycles remain a documented Phase 5 baseline."
|
||||
"next_action": "Run /gsd-resume-work, then execute Plan 03-18 only after reviewing the existing plan-checker pass.",
|
||||
"context_notes": "Plan 03-17 was executed before the latest pause instruction arrived. Plans 03-18 and 03-19 have passed plan review but were not executed. Preserve unrelated working-tree changes and do not modify user-owned decorator placement."
|
||||
}
|
||||
|
||||
@ -150,6 +150,6 @@ None yet.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-09-11T15:17:27Z
|
||||
Stopped at: Phase 3 structural corrections verified; ready to continue Phase 4
|
||||
Resume file: None
|
||||
Last session: 2026-09-11T09:34:07.509Z
|
||||
Stopped at: Session resumed, rechecking Phase 3 Plans 03-17 through 03-19
|
||||
Resume file: .planning/phases/03-data-completion/.continue-here.md
|
||||
|
||||
@ -1,50 +1,37 @@
|
||||
---
|
||||
context: phase
|
||||
phase: 03-data-completion
|
||||
task: 0
|
||||
total_tasks: 3
|
||||
task: 17
|
||||
total_tasks: 19
|
||||
status: paused
|
||||
last_updated: 2026-09-11T09:34:07.509Z
|
||||
last_updated: 2026-09-11T14:33:17.222Z
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
The latest user review identified seven further issues in the completed Phase 3 correction. New Plans `03-17`, `03-18`, and `03-19` were revised but have not yet been rechecked or executed.
|
||||
The latest correction batch is paused after Plan `03-17` completed. Plans `03-18` and `03-19` were plan-checked but must not be executed until the user resumes.
|
||||
|
||||
## New Correction Scope
|
||||
## Completed
|
||||
|
||||
- `03-17`: remove command-level `shouldReplay`, await movement/path controllers, merge directional commands, remove `prettier-ignore`.
|
||||
- `03-18`: make `event/index.ts` exports-only, replace registration factories with explicit classes, move `eventTouchFront` to the hero event layer, preserve public exports.
|
||||
- `03-19`: enforce multiline JSDoc for every touched function and method, with source-aware audits.
|
||||
- Plan `03-17` removed all command-level `shouldReplay` calls.
|
||||
- Movement and pathfinding replay commands now await controller completion.
|
||||
- Four directional replay commands were merged into one parameterized class.
|
||||
- All `prettier-ignore` directives in replay commands were removed.
|
||||
- Plan summary: `.planning/phases/03-data-completion/03-17-SUMMARY.md`.
|
||||
|
||||
## Required Next Order
|
||||
## Remaining
|
||||
|
||||
1. Run `gsd-plan-checker` against Plans `03-17` through `03-19` after the latest revisions.
|
||||
2. If the checker passes, execute `03-17`, then `03-18`, then `03-19`.
|
||||
3. Run the full data suite, Node replay, type gate, ESLint, Prettier, and verifier.
|
||||
- `03-18`: class-based event registrations, exports-only `event/index.ts`, hero-owned `eventTouchFront`.
|
||||
- `03-19`: multiline JSDoc and style correction.
|
||||
|
||||
## Important Decisions
|
||||
## Important Caveat
|
||||
|
||||
- Replay commands must not call `shouldReplay`; the user will add decorators to lower-level state mutation methods.
|
||||
- Movement and pathfinding replay commands must await controller completion before advancing the route.
|
||||
- Four directions share one parameterized command class.
|
||||
- Event registrations use explicit classes; `eventTouchFront` belongs to hero events.
|
||||
- `event/index.ts` may contain exports only.
|
||||
- Do not modify legacy/save architecture, the seven deferred compatibility cycles, or unrelated working-tree changes.
|
||||
Plan `03-17` reported that its exact scope script is incompatible with Windows PowerShell 5.1; an equivalent compatibility check passed. Review this before relying on the scope guard again.
|
||||
|
||||
## Required Reading
|
||||
## User-Owned Boundary
|
||||
|
||||
1. `dev.md`
|
||||
2. `.planning/phases/03-data-completion/03-CONTEXT.md`
|
||||
3. `.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md`
|
||||
4. `.planning/phases/03-data-completion/03-EVENT-CONTRACT.md`
|
||||
5. `.planning/phases/03-data-completion/03-VERIFICATION.md`
|
||||
6. `03-17-PLAN.md`, `03-18-PLAN.md`, `03-19-PLAN.md`
|
||||
|
||||
## Uncommitted Files
|
||||
|
||||
The working tree contains revised planning artifacts plus pre-existing source/test files. Preserve them unless a reviewed plan explicitly owns the path.
|
||||
Do not add or relocate `@shouldReplay()` decorators. The user owns placement on lower-level state mutation methods.
|
||||
|
||||
## Resume
|
||||
|
||||
Run `/gsd-resume-work` to restore this checkpoint. Do not execute the correction plans before the plan checker passes.
|
||||
Run `/gsd-resume-work`. Do not execute `03-18` or `03-19` before the user resumes.
|
||||
|
||||
@ -73,7 +73,54 @@ Output: Corrected replay command source, regression tests, and a replay contract
|
||||
</context>
|
||||
|
||||
<scope_guard>
|
||||
Before the first edit, snapshot the current `git status --porcelain=v1 --untracked-files=all` path list and the bytes/diffs of the pre-existing working tree into `$env:TEMP\mota-phase03-17-scope-baseline`; also save a forbidden-path diff for `packages-user/data-state/src/legacy/**`, `packages-user/data-common/src/save/**`, and `packages-user/data-base/src/hero/attribute.ts`. Do not reset, stash, clean, or overwrite unrelated changes. The final task must compare the post-task path list and forbidden-path diff with those snapshots, fail on any newly changed path outside this plan's allowlist or any changed forbidden diff, and inspect the diff hunks for decorator-placement or unrelated formatting changes. Use a whitespace-aware diff plus `git diff --check`; pre-existing changes are preserved and excluded from the comparison.
|
||||
<powershell><![CDATA[
|
||||
param([ValidateSet('capture','verify')][string]$Mode)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = (git rev-parse --show-toplevel).Trim()
|
||||
$Base = Join-Path $env:TEMP 'mota-phase03-17-scope-baseline'
|
||||
$Allowed = @('packages-user/data-state/src/replay/commands.ts','packages-user/data-state/src/replay/commands.test.ts','.planning/phases/03-data-completion/03-REPLAY-CONTRACT.md')
|
||||
$ForbiddenRoots = @('packages-user/data-state/src/legacy','packages-user/data-common/src/save','packages-user/data-base/src/hero/attribute.ts')
|
||||
$ProtectedBaselineFiles = @('packages-user/data-state/src/core.ts','packages-user/data-common/src/save/system.ts','packages-user/data-state/src/replay/commands.test.ts','packages-user/data-state/src/event/event.test.ts')
|
||||
function Normalize-RepoPath([string]$Path) { $p = $Path.Trim(); if ($p.StartsWith('"') -and $p.EndsWith('"')) { $p = $p.Substring(1,$p.Length-2) -replace '\\"','"' -replace '\\\\','\' }; $p = $p -replace '\\','/'; if ($p.StartsWith('./')) { $p = $p.Substring(2) }; return $p }
|
||||
function Get-StatusRecords { $rows = @(& git -c core.quotePath=false status --porcelain=v1 --untracked-files=all); if ($LASTEXITCODE -ne 0) { throw 'git status failed' }; foreach ($row in $rows) { if ($row.Length -lt 4) { continue }; $raw = $row.Substring(3).Trim(); foreach ($part in ($raw -split ' -> ')) { $p = Normalize-RepoPath $part; if ($p) { [pscustomobject]@{ path=$p; code=$row.Substring(0,2) } } } } }
|
||||
function Get-DiffText([string[]]$Paths,[switch]$IgnoreWhitespace) { $a = @('diff','--no-ext-diff','--binary','--unified=0'); if ($IgnoreWhitespace) { $a += '--ignore-space-at-eol' }; $a += @('HEAD','--'); $a += $Paths; $text = (& git @a 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw 'git diff failed' }; return $text }
|
||||
function Get-TextHash([string]$Text) { $sha = [Security.Cryptography.SHA256]::Create(); return (-join ($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)) | ForEach-Object { $_.ToString('x2') })) }
|
||||
function Get-Key([string]$Path) { return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Path)).TrimEnd('=').Replace('/','_').Replace('+','-') }
|
||||
function Get-FileState([string]$Path) { $f = Join-Path $Root ($Path -replace '/','\'); if (!(Test-Path -LiteralPath $f -PathType Leaf)) { return [pscustomobject]@{ exists=$false; length=0; sha256='' } }; $h = Get-FileHash -Algorithm SHA256 -LiteralPath $f; return [pscustomobject]@{ exists=$true; length=(Get-Item -LiteralPath $f).Length; sha256=$h.Hash.ToLowerInvariant() } }
|
||||
function Get-Record([string]$Path,[string]$Code) { $s = Get-FileState $Path; $d = Get-DiffText @($Path); return [pscustomobject]@{ path=$Path; code=$Code; exists=$s.exists; length=$s.length; sha256=$s.sha256; hunkSha256=(Get-TextHash $d) } }
|
||||
function Is-Forbidden([string]$Path) { return ($Path -like 'packages-user/data-state/src/legacy/*' -or $Path -like 'packages-user/data-common/src/save/*' -or $Path -eq 'packages-user/data-base/src/hero/attribute.ts') }
|
||||
if ($Mode -eq 'capture') {
|
||||
if (Test-Path -LiteralPath $Base) { Remove-Item -LiteralPath $Base -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $Base 'files') | Out-Null; New-Item -ItemType Directory -Force -Path (Join-Path $Base 'hunks') | Out-Null
|
||||
$records = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { $r = Get-Record $_.path $_.code; $key = Get-Key $_.path; $f = Join-Path $Root ($_.path -replace '/','\'); if ($r.exists) { Copy-Item -LiteralPath $f -Destination (Join-Path $Base "files\$key.bin") }; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.patch") -Value (Get-DiffText @($_.path)) -Encoding UTF8; $r })
|
||||
$records | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $Base 'manifest.json') -Encoding UTF8
|
||||
Set-Content -LiteralPath (Join-Path $Base 'forbidden.patch') -Value (Get-DiffText $ForbiddenRoots) -Encoding UTF8
|
||||
Write-Host "Captured normalized paths, per-file bytes, per-file hunks, and forbidden diff at $Base"
|
||||
exit 0
|
||||
}
|
||||
$before = @(Get-Content -Raw -LiteralPath (Join-Path $Base 'manifest.json') | ConvertFrom-Json)
|
||||
$beforePaths = @($before | ForEach-Object path | Sort-Object -Unique)
|
||||
$afterRecords = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { Get-Record $_.path $_.code })
|
||||
$afterPaths = @($afterRecords | ForEach-Object path | Sort-Object -Unique)
|
||||
$newPaths = @($afterPaths | Where-Object { $beforePaths -notcontains $_ }); $missingPaths = @($beforePaths | Where-Object { $afterPaths -notcontains $_ })
|
||||
if (@($newPaths | Where-Object { $Allowed -notcontains $_ }).Count) { throw "New changed path outside allowlist: $($newPaths -join ', ')" }
|
||||
if ($missingPaths.Count) { throw "Pre-existing changed path disappeared: $($missingPaths -join ', ')" }
|
||||
$comparePaths = @($beforePaths + $afterPaths + $ProtectedBaselineFiles | Sort-Object -Unique)
|
||||
$deltaTexts = @()
|
||||
foreach ($path in $comparePaths) {
|
||||
$old = @($before | Where-Object path -eq $path)[0]; $now = @($afterRecords | Where-Object path -eq $path)[0]; if (!$old -or !$now) { continue }
|
||||
$bytesChanged = ($old.exists -ne $now.exists -or $old.length -ne $now.length -or $old.sha256 -ne $now.sha256); $hunksChanged = ($old.hunkSha256 -ne $now.hunkSha256)
|
||||
$key = Get-Key $path; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($path -replace '/','\'); if ($bytesChanged -and (Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $delta = (& git diff --no-index --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw "baseline byte diff failed for $path" }; $deltaTexts += $delta; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.baseline-to-current.patch") -Value $delta -Encoding UTF8 }
|
||||
if ((Is-Forbidden $path) -and ($bytesChanged -or $hunksChanged)) { throw "Forbidden path diff/bytes changed: $path" }
|
||||
if (($Allowed -notcontains $path) -and ($bytesChanged -or $hunksChanged)) { throw "Unrelated pre-existing file diff/bytes changed: $path" }
|
||||
}
|
||||
$beforeForbidden = Get-Content -Raw -LiteralPath (Join-Path $Base 'forbidden.patch'); $afterForbidden = Get-DiffText $ForbiddenRoots; if ($beforeForbidden -cne $afterForbidden) { throw 'Forbidden-path diff changed' }
|
||||
if (Test-Path -LiteralPath 'packages-user/data-state/src/legacy') { $legacyNew = @($afterPaths | Where-Object { $_ -like 'packages-user/data-state/src/legacy/*' -and $beforePaths -notcontains $_ }); if ($legacyNew.Count) { throw 'New legacy path detected' } }
|
||||
$deltaText = $deltaTexts -join "`n"; if ($deltaText -match 'prettier-ignore') { throw 'Manual formatter suppression detected in new diff hunk' }; foreach ($deltaPath in @($beforePaths | Where-Object { $Allowed -contains $_ })) { $key = Get-Key $deltaPath; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($deltaPath -replace '/','\'); if ((Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $check = & git diff --no-index --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $check -match 'whitespace error') { throw "git diff --check failed for plan delta: $deltaPath" }; $wsCheck = & git diff --no-index --ignore-space-at-eol --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $wsCheck -match 'whitespace error') { throw "whitespace-aware diff check failed for plan delta: $deltaPath" } } }
|
||||
Write-Host 'Scope verified: normalized path set, baseline bytes/hunks, forbidden diff, and unrelated working-tree changes are preserved.'
|
||||
]]></powershell>
|
||||
<run>Save the exact block as `$env:TEMP\mota-phase03-17-scope-guard.ps1`; run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-17-scope-guard.ps1" -Mode capture` before the first edit and the same command with `-Mode verify` after the final edit.</run>
|
||||
<fails_when>Capture or verify exits non-zero; normalized paths cannot be read, a literal leading `./` is not removed, or a `.planning/...` path is stripped or misclassified; a new non-allowlisted path appears; a baseline path disappears; any per-file baseline bytes or hunks change outside the allowlist; the forbidden legacy/save/attribute diff or bytes change; a new formatter-suppression hunk, ordinary or whitespace-aware diff-check error, or unrelated working-tree change is detected.</fails_when>
|
||||
</scope_guard>
|
||||
|
||||
## Source Audit
|
||||
@ -95,7 +142,7 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-08, CORR-03-09, CORR-03-10</gap_id>
|
||||
<files>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md</files>
|
||||
<read_first>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-common/src/replay/sandbox.ts, packages-user/data-system/src/path/types.ts, packages-user/data-system/src/path/system.ts, packages-user/data-base/src/hero/attribute.ts, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md</read_first>
|
||||
<action>At task start, write the scope snapshot required by the plan scope guard. Remove the replay-safety decorator helper import and all constructor-time wrapper calls from the command implementation; do not add a replacement decorator, move a decorator to a state class, or edit `HeroAttribute.set`, `HeroAttribute.mul`, or any other user-owned mutation method (CORR-03-08, S-04, S-05). Replace the four directional command bodies with one `ReplayDirectionCommand` receiving the existing `IReplayCommandState` and a `FaceDirection`, storing that direction, starting the mover, awaiting the returned controller's `onEnd`, and resolving `true` only after completion; a missing, active, failed, or rejected move resolves `false`. Change auto-pathfinding to await `IPathfindingController.controller.onEnd` before resolving, retaining numeric parameter validation and null-result failure. Keep item, equip, and unequip state calls synchronous behind the unchanged `Promise<boolean>` interface. Replace the stale command tests with real deferred-controller sequence assertions proving the next replay step cannot start before directional or pathfinding completion, and assert the four directional entries use one constructor while carrying distinct directions. Update `03-REPLAY-CONTRACT.md` so its supersession and completion sections state S-05, remove the stale synchronous completion wording, and preserve the public replay interfaces, route format, stable codes, legacy/save paths, and user-owned decorator placement (CORR-03-09, CORR-03-10, D-07, D-08, D-25).</action>
|
||||
<action>At task start, run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-17-scope-guard.ps1" -Mode capture` exactly as defined in the scope guard. Remove the replay-safety decorator helper import and all constructor-time wrapper calls from the command implementation; do not add a replacement decorator, move a decorator to a state class, or edit `HeroAttribute.set`, `HeroAttribute.mul`, or any other user-owned mutation method (CORR-03-08, S-04, S-05). Replace the four directional command bodies with one `ReplayDirectionCommand` receiving the existing `IReplayCommandState` and a `FaceDirection`, storing that direction, starting the mover, awaiting the returned controller's `onEnd`, and resolving `true` only after completion; a missing, active, failed, or rejected move resolves `false`. Change auto-pathfinding to await `IPathfindingController.controller.onEnd` before resolving, retaining numeric parameter validation and null-result failure. Keep item, equip, and unequip state calls synchronous behind the unchanged `Promise<boolean>` interface. Replace the stale command tests with real deferred-controller sequence assertions proving the next replay step cannot start before directional or pathfinding completion, and assert the four directional entries use one constructor while carrying distinct directions. Update `03-REPLAY-CONTRACT.md` so its supersession and completion sections state S-05, remove the stale synchronous completion wording, and preserve the public replay interfaces, route format, stable codes, legacy/save paths, and user-owned decorator placement (CORR-03-09, CORR-03-10, D-07, D-08, D-25).</action>
|
||||
<verify>
|
||||
<automated>$lines = Get-Content -LiteralPath 'packages-user/data-state/src/replay/commands.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } }; pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts</automated>
|
||||
<fails_when>The Chinese-coverage scan or focused Vitest run exits non-zero, a deferred movement/pathfinding command resolves before its controller completion signal, the next-step assertion observes early execution, the four directional entries use different command constructors, command code owns replay-safety decoration, or any user-owned decorator-placement assertion changes.</fails_when>
|
||||
@ -113,10 +160,10 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-11</gap_id>
|
||||
<files>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts</files>
|
||||
<read_first>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-common/src/replay/system.ts, packages-user/data-common/src/replay/sandbox.ts, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, dev.md</read_first>
|
||||
<action>Rewrite the ordered registry construction as ordinary formatted object literals with direct `new ReplayDirectionCommand(state, direction)` and direct construction of the five non-directional command classes. Remove manual formatter-suppression comments from this registry and let the repository formatter decide line breaks. Extend the source/behavior regression to assert stable enum order, fresh command instances, one directional constructor name, absence of command-layer replay-safety helper calls, awaited controller ordering, and unchanged custom `IReplayCommandItem` registration. Keep generic replay-safety helper tests separate from production command construction, preserve a Chinese comment immediately before every `it`, and leave legacy/save files, `packages-user/data-base/src/hero/attribute.ts`, `ReplaySystem`, `ReplaySandbox`, and all route/enum interfaces untouched (CORR-03-08, CORR-03-10, CORR-03-11, D-04, S-04, S-05). At the end, compare the working tree with the Task 1 scope snapshot and reject newly introduced forbidden paths, decorator-placement changes, or unrelated formatting while retaining all pre-existing changes.</action>
|
||||
<action>Rewrite the ordered registry construction as ordinary formatted object literals with direct `new ReplayDirectionCommand(state, direction)` and direct construction of the five non-directional command classes. Remove manual formatter-suppression comments from this registry and let the repository formatter decide line breaks. Extend the source/behavior regression to assert stable enum order, fresh command instances, one directional constructor name, absence of command-layer replay-safety helper calls, awaited controller ordering, and unchanged custom `IReplayCommandItem` registration. Keep generic replay-safety helper tests separate from production command construction, preserve a Chinese comment immediately before every `it`, and leave legacy/save files, `packages-user/data-base/src/hero/attribute.ts`, `ReplaySystem`, `ReplaySandbox`, and all route/enum interfaces untouched (CORR-03-08, CORR-03-10, CORR-03-11, D-04, S-04, S-05). At the end, run the exact scope-guard script in `-Mode verify`; it compares normalized path sets, saved per-file baseline bytes, baseline hunk hashes, baseline-to-current per-file deltas, and the saved forbidden-path patch without resetting, stashing, cleaning, or overwriting unrelated existing changes.</action>
|
||||
<verify>
|
||||
<automated>$commands = Get-Content -Raw 'packages-user/data-state/src/replay/commands.ts'; if ($commands -match 'shouldReplay') { throw 'Replay command source still invokes the replay-safety helper' }; if ($commands -match 'prettier-ignore') { throw 'Replay registry still contains manual formatter suppression' }; if ($commands -match 'class\s+Replay(?:Up|Right|Down|Left)Command\b') { throw 'Duplicate directional command classes remain' }; if ($commands -notmatch 'class\s+ReplayDirectionCommand\b') { throw 'Parameterized directional command class is missing' }; $test = Get-Content -Raw 'packages-user/data-state/src/replay/commands.test.ts'; if ($test -notmatch 'ReplayDirectionCommand') { throw 'Directional constructor regression is missing' }; $lines = Get-Content -LiteralPath 'packages-user/data-state/src/replay/commands.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } }; pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }; if ($?) { pnpm test:data-node }; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec vitest run script/check-data-circular.test.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/replay/commands.ts packages-user/data-state/src/replay/commands.test.ts }</automated>
|
||||
<fails_when>Any structural assertion finds a command-layer replay-safety helper call, manual formatter suppression, or duplicate directional class; the Chinese-comment scan, focused/full data tests, Node replay, scoped type gate, circular-classifier fixture tests, or Prettier check exits non-zero.</fails_when>
|
||||
<fails_when>Any structural assertion finds a command-layer replay-safety helper call, manual formatter suppression, or duplicate directional class; the Chinese-comment scan, focused/full data tests, Node replay, scoped type gate, circular-classifier fixture tests, Prettier check, or `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-17-scope-guard.ps1" -Mode verify` exits non-zero; the guard must fail on a newly changed non-allowlisted path, a disappeared baseline path, changed baseline bytes/hunks outside the allowlist, a changed legacy/save/attribute forbidden diff, a decorator-placement hunk, or unrelated formatting.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- The registry has no manually inserted formatter-suppression comments and remains in the exact `REPLAY_COMMAND_ORDER` (CORR-03-11, D-08, D-25).
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
phase: 03-data-completion
|
||||
plan: 18
|
||||
type: execute
|
||||
wave: 10
|
||||
depends_on: [03-12, 03-13, 03-16]
|
||||
wave: 11
|
||||
depends_on: [03-12, 03-13, 03-16, 03-17]
|
||||
files_modified:
|
||||
- packages-user/data-state/src/event/index.ts
|
||||
- packages-user/data-state/src/event/map.ts
|
||||
@ -83,7 +83,54 @@ Output: Export-only event index, module-owned registration classes, separate reg
|
||||
</context>
|
||||
|
||||
<scope_guard>
|
||||
Before the first edit, snapshot the current `git status --porcelain=v1 --untracked-files=all` path list and the bytes/diffs of the pre-existing working tree into `$env:TEMP\mota-phase03-18-scope-baseline`; also save a forbidden-path diff for `packages-user/data-state/src/legacy/**`, `packages-user/data-common/src/save/**`, and `packages-user/data-base/src/hero/attribute.ts`. Do not reset, stash, clean, or overwrite unrelated changes, including the preceding replay correction. The final task must compare the post-task path list and forbidden-path diff with those snapshots, fail on any newly changed path outside this plan's allowlist or any changed forbidden diff, and inspect the diff hunks for decorator-placement or unrelated formatting changes. Use a whitespace-aware diff plus `git diff --check`; pre-existing changes are preserved and excluded from the comparison.
|
||||
<powershell><![CDATA[
|
||||
param([ValidateSet('capture','verify')][string]$Mode)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = (git rev-parse --show-toplevel).Trim()
|
||||
$Base = Join-Path $env:TEMP 'mota-phase03-18-scope-baseline'
|
||||
$Allowed = @('packages-user/data-state/src/event/index.ts','packages-user/data-state/src/event/map.ts','packages-user/data-state/src/event/hero.ts','packages-user/data-state/src/event/event.ts','packages-user/data-state/src/event/registrations.ts','packages-user/data-state/src/event/event.test.ts','packages-user/data-state/src/core.ts','packages-user/data-state/src/index.ts','.planning/phases/03-data-completion/03-EVENT-CONTRACT.md')
|
||||
$ForbiddenRoots = @('packages-user/data-state/src/legacy','packages-user/data-common/src/save','packages-user/data-base/src/hero/attribute.ts')
|
||||
$ProtectedBaselineFiles = @('packages-user/data-state/src/core.ts','packages-user/data-common/src/save/system.ts','packages-user/data-state/src/replay/commands.test.ts','packages-user/data-state/src/event/event.test.ts')
|
||||
function Normalize-RepoPath([string]$Path) { $p = $Path.Trim(); if ($p.StartsWith('"') -and $p.EndsWith('"')) { $p = $p.Substring(1,$p.Length-2) -replace '\\"','"' -replace '\\\\','\' }; $p = $p -replace '\\','/'; if ($p.StartsWith('./')) { $p = $p.Substring(2) }; return $p }
|
||||
function Get-StatusRecords { $rows = @(& git -c core.quotePath=false status --porcelain=v1 --untracked-files=all); if ($LASTEXITCODE -ne 0) { throw 'git status failed' }; foreach ($row in $rows) { if ($row.Length -lt 4) { continue }; $raw = $row.Substring(3).Trim(); foreach ($part in ($raw -split ' -> ')) { $p = Normalize-RepoPath $part; if ($p) { [pscustomobject]@{ path=$p; code=$row.Substring(0,2) } } } } }
|
||||
function Get-DiffText([string[]]$Paths,[switch]$IgnoreWhitespace) { $a = @('diff','--no-ext-diff','--binary','--unified=0'); if ($IgnoreWhitespace) { $a += '--ignore-space-at-eol' }; $a += @('HEAD','--'); $a += $Paths; $text = (& git @a 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw 'git diff failed' }; return $text }
|
||||
function Get-TextHash([string]$Text) { $sha = [Security.Cryptography.SHA256]::Create(); return (-join ($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)) | ForEach-Object { $_.ToString('x2') })) }
|
||||
function Get-Key([string]$Path) { return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Path)).TrimEnd('=').Replace('/','_').Replace('+','-') }
|
||||
function Get-FileState([string]$Path) { $f = Join-Path $Root ($Path -replace '/','\'); if (!(Test-Path -LiteralPath $f -PathType Leaf)) { return [pscustomobject]@{ exists=$false; length=0; sha256='' } }; $h = Get-FileHash -Algorithm SHA256 -LiteralPath $f; return [pscustomobject]@{ exists=$true; length=(Get-Item -LiteralPath $f).Length; sha256=$h.Hash.ToLowerInvariant() } }
|
||||
function Get-Record([string]$Path,[string]$Code) { $s = Get-FileState $Path; $d = Get-DiffText @($Path); return [pscustomobject]@{ path=$Path; code=$Code; exists=$s.exists; length=$s.length; sha256=$s.sha256; hunkSha256=(Get-TextHash $d) } }
|
||||
function Is-Forbidden([string]$Path) { return ($Path -like 'packages-user/data-state/src/legacy/*' -or $Path -like 'packages-user/data-common/src/save/*' -or $Path -eq 'packages-user/data-base/src/hero/attribute.ts') }
|
||||
if ($Mode -eq 'capture') {
|
||||
if (Test-Path -LiteralPath $Base) { Remove-Item -LiteralPath $Base -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $Base 'files') | Out-Null; New-Item -ItemType Directory -Force -Path (Join-Path $Base 'hunks') | Out-Null
|
||||
$records = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { $r = Get-Record $_.path $_.code; $key = Get-Key $_.path; $f = Join-Path $Root ($_.path -replace '/','\'); if ($r.exists) { Copy-Item -LiteralPath $f -Destination (Join-Path $Base "files\$key.bin") }; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.patch") -Value (Get-DiffText @($_.path)) -Encoding UTF8; $r })
|
||||
$records | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $Base 'manifest.json') -Encoding UTF8
|
||||
Set-Content -LiteralPath (Join-Path $Base 'forbidden.patch') -Value (Get-DiffText $ForbiddenRoots) -Encoding UTF8
|
||||
Write-Host "Captured normalized paths, per-file bytes, per-file hunks, and forbidden diff at $Base"
|
||||
exit 0
|
||||
}
|
||||
$before = @(Get-Content -Raw -LiteralPath (Join-Path $Base 'manifest.json') | ConvertFrom-Json)
|
||||
$beforePaths = @($before | ForEach-Object path | Sort-Object -Unique)
|
||||
$afterRecords = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { Get-Record $_.path $_.code })
|
||||
$afterPaths = @($afterRecords | ForEach-Object path | Sort-Object -Unique)
|
||||
$newPaths = @($afterPaths | Where-Object { $beforePaths -notcontains $_ }); $missingPaths = @($beforePaths | Where-Object { $afterPaths -notcontains $_ })
|
||||
if (@($newPaths | Where-Object { $Allowed -notcontains $_ }).Count) { throw "New changed path outside allowlist: $($newPaths -join ', ')" }
|
||||
if ($missingPaths.Count) { throw "Pre-existing changed path disappeared: $($missingPaths -join ', ')" }
|
||||
$comparePaths = @($beforePaths + $afterPaths + $ProtectedBaselineFiles | Sort-Object -Unique)
|
||||
$deltaTexts = @()
|
||||
foreach ($path in $comparePaths) {
|
||||
$old = @($before | Where-Object path -eq $path)[0]; $now = @($afterRecords | Where-Object path -eq $path)[0]; if (!$old -or !$now) { continue }
|
||||
$bytesChanged = ($old.exists -ne $now.exists -or $old.length -ne $now.length -or $old.sha256 -ne $now.sha256); $hunksChanged = ($old.hunkSha256 -ne $now.hunkSha256)
|
||||
$key = Get-Key $path; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($path -replace '/','\'); if ($bytesChanged -and (Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $delta = (& git diff --no-index --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw "baseline byte diff failed for $path" }; $deltaTexts += $delta; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.baseline-to-current.patch") -Value $delta -Encoding UTF8 }
|
||||
if ((Is-Forbidden $path) -and ($bytesChanged -or $hunksChanged)) { throw "Forbidden path diff/bytes changed: $path" }
|
||||
if (($Allowed -notcontains $path) -and ($bytesChanged -or $hunksChanged)) { throw "Unrelated pre-existing file diff/bytes changed: $path" }
|
||||
}
|
||||
$beforeForbidden = Get-Content -Raw -LiteralPath (Join-Path $Base 'forbidden.patch'); $afterForbidden = Get-DiffText $ForbiddenRoots; if ($beforeForbidden -cne $afterForbidden) { throw 'Forbidden-path diff changed' }
|
||||
if (Test-Path -LiteralPath 'packages-user/data-state/src/event/runtime.ts') { throw 'runtime.ts must not be created' }
|
||||
$deltaText = $deltaTexts -join "`n"; if ($deltaText -match 'prettier-ignore') { throw 'Manual formatter suppression detected in new diff hunk' }; foreach ($deltaPath in @($beforePaths | Where-Object { $Allowed -contains $_ })) { $key = Get-Key $deltaPath; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($deltaPath -replace '/','\'); if ((Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $check = & git diff --no-index --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $check -match 'whitespace error') { throw "git diff --check failed for plan delta: $deltaPath" }; $wsCheck = & git diff --no-index --ignore-space-at-eol --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $wsCheck -match 'whitespace error') { throw "whitespace-aware diff check failed for plan delta: $deltaPath" } } }
|
||||
Write-Host 'Scope verified: normalized path set, baseline bytes/hunks, forbidden diff, and unrelated working-tree changes are preserved.'
|
||||
]]></powershell>
|
||||
<run>Save the exact block as `$env:TEMP\mota-phase03-18-scope-guard.ps1`; run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-18-scope-guard.ps1" -Mode capture` before the first edit and the same command with `-Mode verify` after the final edit.</run>
|
||||
<fails_when>Capture or verify exits non-zero; normalized porcelain paths are compared incorrectly, a literal leading `./` is not removed, or a `.planning/...` path is stripped or misclassified; a new non-allowlisted path appears; a baseline path disappears; any per-file baseline bytes or hunks change outside the allowlist, including `core.ts` or existing save/test files; the forbidden legacy/save/attribute diff or bytes change; `runtime.ts`, a new formatter-suppression hunk, ordinary or whitespace-aware diff-check error, or unrelated working-tree change is detected.</fails_when>
|
||||
</scope_guard>
|
||||
|
||||
## Source Audit
|
||||
@ -105,8 +152,8 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-13</gap_id>
|
||||
<files>packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts</files>
|
||||
<read_first>packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/event/types.ts, packages-user/data-system/src/event/types.ts, packages-user/data-system/src/event/executor.ts, packages-user/data-common/src/event/types.ts, packages-user/data-base/src/map/types.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md</read_first>
|
||||
<action>At task start, write the scope snapshot required by the plan scope guard. Define explicit registration classes beside the handlers: exactly three map classes in `map.ts` for set/move/delete, exactly three hero classes in `hero.ts` for move/move-step/front touch, and exactly two control classes in `event.ts` for `eventInsertEvents` and direct `Statement[]` `eventInsertEvent`. Each class must implement the existing AnonTokyo built-in shape and own a typed stable `name` field plus the corresponding `func` field; do not route registration through a generic handler factory or opaque descriptor array. Move `eventTouchFront`, its event-source collection helpers, and its registration ownership into `hero.ts`; keep its current OnTouch ordering, environment construction, no-movement behavior, and awaited executor call. Do not create or modify `event/runtime.ts`; keep any required target-resolution helper in an already-owned event module so file creation and reads remain deterministic. Keep `event.ts` responsible only for the two insertion operations, preserving the nested-depth guard, direct `Statement[]` interpreter path, and store/interpreter semantics. Do not alter event parameter interfaces, event scope, legacy/save paths, or user-owned decorator placement (CORR-03-13, D-10, D-11, D-24, D-27, S-01, S-04).</action>
|
||||
<verify>
|
||||
<action>At task start, run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-18-scope-guard.ps1" -Mode capture` exactly as defined in the scope guard. Define explicit registration classes beside the handlers: exactly three map classes in `map.ts` for set/move/delete, exactly three hero classes in `hero.ts` for move/move-step/front touch, and exactly two control classes in `event.ts` for `eventInsertEvents` and direct `Statement[]` `eventInsertEvent`. Each class must implement the existing AnonTokyo built-in shape and own a typed stable `name` field plus the corresponding `func` field; do not route registration through a generic handler factory or opaque descriptor array. Move `eventTouchFront`, its event-source collection helpers, and its registration ownership into `hero.ts`; keep its current OnTouch ordering, environment construction, no-movement behavior, and awaited executor call. Do not create or modify `event/runtime.ts`; keep any required target-resolution helper in an already-owned event module so file creation and reads remain deterministic. Keep `event.ts` responsible only for the two insertion operations, preserving the nested-depth guard, direct `Statement[]` interpreter path, and store/interpreter semantics. Do not alter event parameter interfaces, event scope, legacy/save paths, or user-owned decorator placement (CORR-03-13, D-10, D-11, D-24, D-27, S-01, S-04).</action>
|
||||
<verify>
|
||||
<automated>pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts</automated>
|
||||
<fails_when>Focused event or dispatch tests exit non-zero, front-touch registration remains owned by event.ts, a registration class does not expose its own stable name/function pair, event ordering/awaiting/direct-body semantics change, or a new import cycle prevents the event path from loading.</fails_when>
|
||||
</verify>
|
||||
@ -123,14 +170,16 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-12, CORR-03-13</gap_id>
|
||||
<files>packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/registrations.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md</files>
|
||||
<read_first>packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, packages-user/data-system/src/event/system.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md</read_first>
|
||||
<action>Create `event/registrations.ts` as the sole assembly module. Its factory must return exactly eight direct, readable class instances in this order and ownership: map — `SetBlockEventRegistration`, `MoveBlockEventRegistration`, `DeleteBlockEventRegistration`; hero — `MoveHeroEventRegistration`, `MoveHeroStepEventRegistration`, `TouchFrontEventRegistration`; control — `InsertEventsEventRegistration`, `InsertEventEventRegistration`. Change CoreState to import this factory from the assembly module rather than relying on the event barrel. Reduce `event/index.ts` to export statements only, including a compatible re-export of the assembly factory if that symbol was previously reachable, without importing or invoking registration code there. Keep `data-state/src/index.ts` export-only and preserve every public event symbol currently reachable through it with the same path via compatible re-exports/wrappers; it must not become a registration owner. Remove all generic/per-module assembly builders once the direct list is wired. Update `03-EVENT-CONTRACT.md` to record the exact eight-entry class ownership, deterministic assembly owner, export-only barrels, hero front touch, direct `Statement[]` behavior, and unchanged public contracts (CORR-03-12, CORR-03-13, D-10, D-11, S-03, S-01).</action>
|
||||
<action>Create `event/registrations.ts` as the sole assembly module. Enumerate every currently reachable public registration export explicitly: `createEventBuiltinRegistrations`, `createMapEventBuiltinRegistrations`, `createHeroEventBuiltinRegistrations`, and `createControlEventBuiltinRegistrations`. Define all four exports in `registrations.ts` with their existing compatible call signatures: the three group creators each return fresh direct instances for their own group, and `createEventBuiltinRegistrations` returns the concatenation of map, hero, and control groups in the exact eight-entry order. The four names must remain reachable through `event/index.ts` and `data-state/src/index.ts` by explicit re-export statements (or signature-preserving wrappers that delegate only to `registrations.ts`); no old public name may remain ambiguous or point at a second assembly owner. The factory must use exactly these direct class instances in this order and ownership: map — `SetBlockEventRegistration`, `MoveBlockEventRegistration`, `DeleteBlockEventRegistration`; hero — `MoveHeroEventRegistration`, `MoveHeroStepEventRegistration`, `TouchFrontEventRegistration`; control — `InsertEventsEventRegistration`, `InsertEventEventRegistration`. Change CoreState to import the aggregate factory from `registrations.ts` rather than relying on the event barrel. Reduce `event/index.ts` to export statements only, including explicit re-exports of all four registration creators without importing or invoking registration code there. Keep `data-state/src/index.ts` export-only and preserve every public event symbol currently reachable through it with compatible re-exports/wrappers; it must not become a registration owner. Remove all generic/per-module assembly builders once the direct list is wired. Update `03-EVENT-CONTRACT.md` to record the exact eight-entry class ownership, the four named public registration exports and their compatibility behavior, deterministic assembly ownership, export-only barrels, hero front touch, direct `Statement[]` behavior, and unchanged public contracts (CORR-03-12, CORR-03-13, D-10, D-11, S-03, S-01).</action>
|
||||
<verify>
|
||||
<export_contract>$registration = Get-Content -Raw 'packages-user/data-state/src/event/registrations.ts'; $barrel = Get-Content -Raw 'packages-user/data-state/src/event/index.ts'; $root = Get-Content -Raw 'packages-user/data-state/src/index.ts'; foreach ($name in @('createEventBuiltinRegistrations','createMapEventBuiltinRegistrations','createHeroEventBuiltinRegistrations','createControlEventBuiltinRegistrations')) { if ($registration -notmatch [regex]::Escape($name) -or $barrel -notmatch [regex]::Escape($name)) { throw "Missing public registration export: $name" } }; if ($barrel -match '(?m)^\s*import\s' -or $root -match '(?m)^\s*import\s') { throw 'Public barrels must remain export-only' }</export_contract>
|
||||
<automated>$index = Get-Content -Raw 'packages-user/data-state/src/event/index.ts'; if ($index -match '(?m)^\s*import\s') { throw 'Event index still imports registration code' }; if ($index -match '(?m)^\s*(?:export\s+)?(?:async\s+)?function\s') { throw 'Event index still declares assembly logic' }; $root = Get-Content -Raw 'packages-user/data-state/src/index.ts'; if ($root -match '(?m)^\s*import\s') { throw 'Data-state root index is not export-only' }; if ($root -notmatch "export \* from './event'") { throw 'Data-state root index no longer exposes event exports' }; $registration = Get-Content -Raw 'packages-user/data-state/src/event/registrations.ts'; $entries = [regex]::Matches($registration, 'new\s+\w+EventRegistration\s*\('); if ($entries.Count -ne 8) { throw "Expected exactly eight registration instances, found $($entries.Count)" }; foreach ($token in @('new SetBlockEventRegistration', 'new MoveBlockEventRegistration', 'new DeleteBlockEventRegistration', 'new MoveHeroEventRegistration', 'new MoveHeroStepEventRegistration', 'new TouchFrontEventRegistration', 'new InsertEventsEventRegistration', 'new InsertEventEventRegistration')) { if ($registration -notmatch [regex]::Escape($token)) { throw "Missing direct registration construction: $token" } }; foreach ($owner in @(@{file='map.ts'; tokens=@('class SetBlockEventRegistration','class MoveBlockEventRegistration','class DeleteBlockEventRegistration')}, @{file='hero.ts'; tokens=@('class MoveHeroEventRegistration','class MoveHeroStepEventRegistration','class TouchFrontEventRegistration')}, @{file='event.ts'; tokens=@('class InsertEventsEventRegistration','class InsertEventEventRegistration') })) { $source = Get-Content -Raw (Join-Path 'packages-user/data-state/src/event' $owner.file); foreach ($token in $owner.tokens) { if ($source -notmatch [regex]::Escape($token)) { throw "Registration ownership mismatch: $token" } } }; $core = Get-Content -Raw 'packages-user/data-state/src/core.ts'; if ($core -notmatch "event/registrations") { throw 'CoreState does not consume the external registration assembly module' }; if (Test-Path 'packages-user/data-state/src/event/runtime.ts') { throw 'runtime.ts is not part of this deterministic design' }; pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/event/index.ts packages-user/data-state/src/event/registrations.ts packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/core.ts packages-user/data-state/src/index.ts .planning/phases/03-data-completion/03-EVENT-CONTRACT.md }</automated>
|
||||
<fails_when>The event or data-state root index contains imports or assembly logic, registrations.ts does not contain exactly eight direct entries in the specified ownership/order, CoreState still consumes barrel-owned assembly, runtime.ts is created/used, focused tests/type gate/Prettier exit non-zero, or the updated contract omits the ownership/public-export boundaries.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `event/index.ts` contains only `export` statements and does not assemble registrations (CORR-03-12).
|
||||
- `registrations.ts` is a readable direct construction list, and CoreState consumes it without a second registration owner (CORR-03-12, CORR-03-13, D-10).
|
||||
- `createEventBuiltinRegistrations`, `createMapEventBuiltinRegistrations`, `createHeroEventBuiltinRegistrations`, and `createControlEventBuiltinRegistrations` are all explicitly defined by or compatibility-delegated to `registrations.ts`, explicitly re-exported through both public barrels, and covered by a test that verifies their signatures, fresh group contents, aggregate order, and absence of a second assembly owner (CORR-03-12, CORR-03-13, D-10).
|
||||
- The contract record explicitly preserves stable order and event semantics while recording hero ownership of front touch (D-11, D-24, D-27).
|
||||
</acceptance_criteria>
|
||||
<done>Event registration assembly has moved out of the barrel into the explicit class-based assembly module and remains wired to GameEventSystem.</done>
|
||||
@ -143,11 +192,12 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<read_first>packages-user/data-state/src/event/event.test.ts, packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/registrations.ts, packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, dev.md</read_first>
|
||||
<action>Extend the focused regression through the real `registrations.ts` assembly and CoreState interpreter. Assert the exact eight names and existing order, the three-map/three-hero/two-control ownership split, that every registration instance owns its own name/function properties, that the front-touch registration constructor originates from the hero module, both event barrels contain exports only, and all public symbols previously reachable from `data-state/src/index.ts` remain reachable through compatible re-exports/wrappers. Retain behavior assertions for map mutations, awaited hero/event actions, front OnTouch ordering/no movement, direct `Statement[]` insertion, id-sequence insertion, and safe missing targets. Add a Chinese single-line comment immediately before every `it` call in the complete test file and run the scanner below. Do not assert or implement any expanded legacy event catalog, save change, replay decorator move, runtime.ts creation, or altered public event parameter contract (CORR-03-12, CORR-03-13, D-03, D-04, D-11, D-24, S-01, S-03, S-04).</action>
|
||||
<verify>
|
||||
<automated>$lines = Get-Content -LiteralPath 'packages-user/data-state/src/event/event.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } }; pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }; if ($?) { pnpm test:data-node }; if ($?) { $status = & git status --porcelain=v1 --untracked-files=all; $statusExit = $LASTEXITCODE; if ($statusExit -ne 0) { throw 'git status failed during scope assertion' }; $baseline = Get-Content -LiteralPath "$env:TEMP\mota-phase03-18-scope-baseline\paths.txt"; $new = Compare-Object -ReferenceObject $baseline -DifferenceObject $status -PassThru; $allowed = @('packages-user/data-state/src/event/index.ts','packages-user/data-state/src/event/map.ts','packages-user/data-state/src/event/hero.ts','packages-user/data-state/src/event/event.ts','packages-user/data-state/src/event/registrations.ts','packages-user/data-state/src/event/event.test.ts','packages-user/data-state/src/core.ts','packages-user/data-state/src/index.ts','.planning/phases/03-data-completion/03-EVENT-CONTRACT.md'); foreach ($delta in $new) { if ($delta.SideIndicator -eq '=>' -and $allowed -notcontains $delta.InputObject) { throw 'Scope assertion found a newly changed path outside Plan 03-18 allowlist' } }; if (Test-Path 'packages-user/data-state/src/event/runtime.ts') { throw 'runtime.ts must not be created' }; git diff --check }</automated>
|
||||
<automated>$lines = Get-Content -LiteralPath 'packages-user/data-state/src/event/event.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } }; pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }; if ($?) { pnpm test:data-node }; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/event/index.ts packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/event/registrations.ts packages-user/data-state/src/event/event.test.ts packages-user/data-state/src/core.ts packages-user/data-state/src/index.ts .planning/phases/03-data-completion/03-EVENT-CONTRACT.md }; if ($?) { powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-18-scope-guard.ps1" -Mode verify }</automated>
|
||||
<fails_when>Any event test lacks its Chinese coverage comment, exact registration ownership/order/public-export or existing event behavior fails, runtime.ts is created, the four-package data suite or independent Node replay exits non-zero, or the baseline-aware scope/diff assertion finds forbidden or unrelated changes.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- The real eight-entry class registry and CoreState interpreter path pass behavior and ownership assertions (CORR-03-13, D-10).
|
||||
- The regression matrix imports and exercises all four reachable registration exports — `createEventBuiltinRegistrations`, `createMapEventBuiltinRegistrations`, `createHeroEventBuiltinRegistrations`, and `createControlEventBuiltinRegistrations` — and proves that every compatible path delegates to the sole `registrations.ts` assembly owner (CORR-03-12, CORR-03-13, D-10).
|
||||
- The existing data suite and Node replay remain green without modifications to legacy/save code or user-owned decorator placement, and direct `Statement[]` insertion remains intact (D-04, D-15, S-01, S-03, S-04).
|
||||
</acceptance_criteria>
|
||||
<done>Event barrel, class ownership, hero front-touch placement, stable order, and existing behavior are regression-tested through the real assembly path.</done>
|
||||
@ -180,7 +230,7 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
- `pnpm test:data-node`
|
||||
- `pnpm exec tsx script/check-data-type.ts`
|
||||
- Scoped ESLint and Prettier checks for the event implementation, assembly, CoreState import, contract, and tests
|
||||
- Baseline-aware mechanical diff/scope assertion from `<scope_guard>`; it rejects newly changed legacy/save paths, `attribute.ts`/decorator placement, runtime.ts, and unrelated formatting while preserving pre-existing working-tree changes
|
||||
- Run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-18-scope-guard.ps1" -Mode verify`; its normalized path/diff baseline explicitly compares saved per-file bytes and hunks for `core.ts` and existing save/test files, the forbidden-path patch, and baseline-to-current deltas while rejecting runtime.ts, decorator placement, or unrelated formatting and preserving pre-existing working-tree changes
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
phase: 03-data-completion
|
||||
plan: 19
|
||||
type: execute
|
||||
wave: 11
|
||||
wave: 12
|
||||
depends_on: [03-17, 03-18]
|
||||
files_modified:
|
||||
- packages-user/data-state/src/replay/commands.ts
|
||||
@ -25,7 +25,7 @@ estimate:
|
||||
confidence: low
|
||||
must_haves:
|
||||
truths:
|
||||
- "CORR-03-14: every touched top-level function and class method in the replay/event correction files has a multiline JSDoc block, proven by an explicit touched-symbol inventory and declaration-aware scan."
|
||||
- "CORR-03-14: every touched top-level function and non-constructor class method in the replay/event correction files has a multiline JSDoc block, while constructors are enumerated and explicitly exempted, proven by an explicit touched-symbol inventory and declaration-aware scan."
|
||||
- "The JSDoc correction is limited to the replay/event files and touched symbols from CORR-03-08 through CORR-03-13; unrelated legacy/save/render files are not reformatted."
|
||||
- "Chinese single-line coverage comments remain immediately before every modified or newly added Vitest it call."
|
||||
- "Replay await semantics under S-05, one directional class, export-only event indexes, class-owned registration assembly, hero front touch, stable order, direct Statement[] insertion, legacy/save boundaries, and user-owned decorator placement remain intact."
|
||||
@ -41,7 +41,7 @@ must_haves:
|
||||
- path: "packages-user/data-state/src/event/registrations.ts"
|
||||
provides: "Multiline function JSDoc for the registration assembly function"
|
||||
- path: "script/check-touched-jsdoc.ts"
|
||||
provides: "Declaration-aware audit covering every touched top-level function and class method, not only top-level function declarations"
|
||||
provides: "Declaration-aware audit covering every touched top-level function and class method, explicitly exempting constructors and requiring multiline JSDoc for all other declarations"
|
||||
key_links:
|
||||
- "JSDoc structural audit -> touched function declarations -> scoped formatter/test gates"
|
||||
- "Chinese test-comment audit -> replay/event regression tests -> reviewable correction evidence"
|
||||
@ -81,7 +81,54 @@ Output: Scoped JSDoc/style cleanup and structural regression checks over the cor
|
||||
</context>
|
||||
|
||||
<scope_guard>
|
||||
Before the first edit, snapshot the current `git status --porcelain=v1 --untracked-files=all` path list and the bytes/diffs of the pre-existing working tree into `$env:TEMP\mota-phase03-19-scope-baseline`; also save a forbidden-path diff for `packages-user/data-state/src/legacy/**`, `packages-user/data-common/src/save/**`, and `packages-user/data-base/src/hero/attribute.ts`. Do not reset, stash, clean, or overwrite unrelated changes from Plans 03-17/03-18. The final task must compare the post-task path list and forbidden-path diff with those snapshots, fail on any newly changed path outside this plan's allowlist or any changed forbidden diff, and inspect the diff hunks for decorator-placement or unrelated formatting changes. Use a whitespace-aware diff plus `git diff --check`; pre-existing changes are preserved and excluded from the comparison.
|
||||
<powershell><![CDATA[
|
||||
param([ValidateSet('capture','verify')][string]$Mode)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$Root = (git rev-parse --show-toplevel).Trim()
|
||||
$Base = Join-Path $env:TEMP 'mota-phase03-19-scope-baseline'
|
||||
$Allowed = @('packages-user/data-state/src/replay/commands.ts','packages-user/data-state/src/replay/commands.test.ts','packages-user/data-state/src/event/map.ts','packages-user/data-state/src/event/hero.ts','packages-user/data-state/src/event/event.ts','packages-user/data-state/src/event/registrations.ts','packages-user/data-state/src/event/event.test.ts','script/check-touched-jsdoc.ts')
|
||||
$ForbiddenRoots = @('packages-user/data-state/src/legacy','packages-user/data-common/src/save','packages-user/data-base/src/hero/attribute.ts')
|
||||
$ProtectedBaselineFiles = @('packages-user/data-state/src/core.ts','packages-user/data-common/src/save/system.ts','packages-user/data-state/src/replay/commands.test.ts','packages-user/data-state/src/event/event.test.ts')
|
||||
function Normalize-RepoPath([string]$Path) { $p = $Path.Trim(); if ($p.StartsWith('"') -and $p.EndsWith('"')) { $p = $p.Substring(1,$p.Length-2) -replace '\\"','"' -replace '\\\\','\' }; $p = $p -replace '\\','/'; if ($p.StartsWith('./')) { $p = $p.Substring(2) }; return $p }
|
||||
function Get-StatusRecords { $rows = @(& git -c core.quotePath=false status --porcelain=v1 --untracked-files=all); if ($LASTEXITCODE -ne 0) { throw 'git status failed' }; foreach ($row in $rows) { if ($row.Length -lt 4) { continue }; $raw = $row.Substring(3).Trim(); foreach ($part in ($raw -split ' -> ')) { $p = Normalize-RepoPath $part; if ($p) { [pscustomobject]@{ path=$p; code=$row.Substring(0,2) } } } } }
|
||||
function Get-DiffText([string[]]$Paths,[switch]$IgnoreWhitespace) { $a = @('diff','--no-ext-diff','--binary','--unified=0'); if ($IgnoreWhitespace) { $a += '--ignore-space-at-eol' }; $a += @('HEAD','--'); $a += $Paths; $text = (& git @a 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw 'git diff failed' }; return $text }
|
||||
function Get-TextHash([string]$Text) { $sha = [Security.Cryptography.SHA256]::Create(); return (-join ($sha.ComputeHash([Text.Encoding]::UTF8.GetBytes($Text)) | ForEach-Object { $_.ToString('x2') })) }
|
||||
function Get-Key([string]$Path) { return [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($Path)).TrimEnd('=').Replace('/','_').Replace('+','-') }
|
||||
function Get-FileState([string]$Path) { $f = Join-Path $Root ($Path -replace '/','\'); if (!(Test-Path -LiteralPath $f -PathType Leaf)) { return [pscustomobject]@{ exists=$false; length=0; sha256='' } }; $h = Get-FileHash -Algorithm SHA256 -LiteralPath $f; return [pscustomobject]@{ exists=$true; length=(Get-Item -LiteralPath $f).Length; sha256=$h.Hash.ToLowerInvariant() } }
|
||||
function Get-Record([string]$Path,[string]$Code) { $s = Get-FileState $Path; $d = Get-DiffText @($Path); return [pscustomobject]@{ path=$Path; code=$Code; exists=$s.exists; length=$s.length; sha256=$s.sha256; hunkSha256=(Get-TextHash $d) } }
|
||||
function Is-Forbidden([string]$Path) { return ($Path -like 'packages-user/data-state/src/legacy/*' -or $Path -like 'packages-user/data-common/src/save/*' -or $Path -eq 'packages-user/data-base/src/hero/attribute.ts') }
|
||||
if ($Mode -eq 'capture') {
|
||||
if (Test-Path -LiteralPath $Base) { Remove-Item -LiteralPath $Base -Recurse -Force }
|
||||
New-Item -ItemType Directory -Force -Path (Join-Path $Base 'files') | Out-Null; New-Item -ItemType Directory -Force -Path (Join-Path $Base 'hunks') | Out-Null
|
||||
$records = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { $r = Get-Record $_.path $_.code; $key = Get-Key $_.path; $f = Join-Path $Root ($_.path -replace '/','\'); if ($r.exists) { Copy-Item -LiteralPath $f -Destination (Join-Path $Base "files\$key.bin") }; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.patch") -Value (Get-DiffText @($_.path)) -Encoding UTF8; $r })
|
||||
$records | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath (Join-Path $Base 'manifest.json') -Encoding UTF8
|
||||
Set-Content -LiteralPath (Join-Path $Base 'forbidden.patch') -Value (Get-DiffText $ForbiddenRoots) -Encoding UTF8
|
||||
Write-Host "Captured normalized paths, per-file bytes, per-file hunks, and forbidden diff at $Base"
|
||||
exit 0
|
||||
}
|
||||
$before = @(Get-Content -Raw -LiteralPath (Join-Path $Base 'manifest.json') | ConvertFrom-Json)
|
||||
$beforePaths = @($before | ForEach-Object path | Sort-Object -Unique)
|
||||
$afterRecords = @(Get-StatusRecords | Sort-Object path -Unique | ForEach-Object { Get-Record $_.path $_.code })
|
||||
$afterPaths = @($afterRecords | ForEach-Object path | Sort-Object -Unique)
|
||||
$newPaths = @($afterPaths | Where-Object { $beforePaths -notcontains $_ }); $missingPaths = @($beforePaths | Where-Object { $afterPaths -notcontains $_ })
|
||||
if (@($newPaths | Where-Object { $Allowed -notcontains $_ }).Count) { throw "New changed path outside allowlist: $($newPaths -join ', ')" }
|
||||
if ($missingPaths.Count) { throw "Pre-existing changed path disappeared: $($missingPaths -join ', ')" }
|
||||
$comparePaths = @($beforePaths + $afterPaths + $ProtectedBaselineFiles | Sort-Object -Unique)
|
||||
$deltaTexts = @()
|
||||
foreach ($path in $comparePaths) {
|
||||
$old = @($before | Where-Object path -eq $path)[0]; $now = @($afterRecords | Where-Object path -eq $path)[0]; if (!$old -or !$now) { continue }
|
||||
$bytesChanged = ($old.exists -ne $now.exists -or $old.length -ne $now.length -or $old.sha256 -ne $now.sha256); $hunksChanged = ($old.hunkSha256 -ne $now.hunkSha256)
|
||||
$key = Get-Key $path; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($path -replace '/','\'); if ($bytesChanged -and (Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $delta = (& git diff --no-index --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String); if ($LASTEXITCODE -notin @(0,1)) { throw "baseline byte diff failed for $path" }; $deltaTexts += $delta; Set-Content -LiteralPath (Join-Path $Base "hunks\$key.baseline-to-current.patch") -Value $delta -Encoding UTF8 }
|
||||
if ((Is-Forbidden $path) -and ($bytesChanged -or $hunksChanged)) { throw "Forbidden path diff/bytes changed: $path" }
|
||||
if (($Allowed -notcontains $path) -and ($bytesChanged -or $hunksChanged)) { throw "Unrelated pre-existing file diff/bytes changed: $path" }
|
||||
}
|
||||
$beforeForbidden = Get-Content -Raw -LiteralPath (Join-Path $Base 'forbidden.patch'); $afterForbidden = Get-DiffText $ForbiddenRoots; if ($beforeForbidden -cne $afterForbidden) { throw 'Forbidden-path diff changed' }
|
||||
if (Test-Path -LiteralPath 'packages-user/data-state/src/event/runtime.ts') { throw 'runtime.ts must not be created' }
|
||||
$deltaText = $deltaTexts -join "`n"; if ($deltaText -match 'prettier-ignore') { throw 'Manual formatter suppression detected in new diff hunk' }; foreach ($deltaPath in @($beforePaths | Where-Object { $Allowed -contains $_ })) { $key = Get-Key $deltaPath; $oldBytes = Join-Path $Base "files\$key.bin"; $newBytes = Join-Path $Root ($deltaPath -replace '/','\'); if ((Test-Path -LiteralPath $oldBytes) -and (Test-Path -LiteralPath $newBytes)) { $check = & git diff --no-index --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $check -match 'whitespace error') { throw "git diff --check failed for plan delta: $deltaPath" }; $wsCheck = & git diff --no-index --ignore-space-at-eol --check --unified=0 -- $oldBytes $newBytes 2>&1 | Out-String; if ($LASTEXITCODE -notin @(0,1) -or $wsCheck -match 'whitespace error') { throw "whitespace-aware diff check failed for plan delta: $deltaPath" } } }
|
||||
Write-Host 'Scope verified: normalized path set, baseline bytes/hunks, forbidden diff, and unrelated working-tree changes are preserved.'
|
||||
]]></powershell>
|
||||
<run>Save the exact block as `$env:TEMP\mota-phase03-19-scope-guard.ps1`; run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-19-scope-guard.ps1" -Mode capture` before the first edit and the same command with `-Mode verify` after the final edit.</run>
|
||||
<fails_when>Capture or verify exits non-zero; normalized porcelain paths are compared incorrectly, a literal leading `./` is not removed, or a `.planning/...` path is stripped or misclassified; a new non-allowlisted path appears; a baseline path disappears; any per-file baseline bytes or hunks change outside the allowlist, including `core.ts` or existing save/test files; the forbidden legacy/save/attribute diff or bytes change; `runtime.ts`, a new formatter-suppression hunk, ordinary or whitespace-aware diff-check error, or unrelated working-tree change is detected.</fails_when>
|
||||
</scope_guard>
|
||||
|
||||
## Source Audit
|
||||
@ -102,13 +149,13 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-14</gap_id>
|
||||
<files>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, script/check-touched-jsdoc.ts</files>
|
||||
<read_first>packages-user/data-state/src/replay/commands.ts, packages-user/data-state/src/replay/commands.test.ts, packages-user/data-state/src/replay/types.ts, packages-user/data-common/src/replay/types.ts, packages-user/data-base/src/hero/attribute.ts, .planning/phases/03-data-completion/03-REPLAY-CONTRACT.md, .planning/phases/03-data-completion/03-CONTEXT.md, dev.md</read_first>
|
||||
<action>At task start, write the scope snapshot required by the plan scope guard. Convert every JSDoc block attached to a replay symbol touched by CORR-03-08 through CORR-03-11 into a multiline block with the description on its own line and the closing marker on its own line. Maintain this explicit touched-symbol inventory: `ReplayDirectionCommand.moveHero`, `ReplayDirectionCommand.execute`, `ReplayAutoPathfindCommand.moveToPoint`, `ReplayAutoPathfindCommand.execute`, `ReplayUseItemCommand.useItem`, `ReplayUseItemCommand.execute`, `ReplayEquipCommand.equip`, `ReplayEquipCommand.execute`, `ReplayUnequipCommand.unequip`, `ReplayUnequipCommand.execute`, `createReplayCommandItems`, and `registerReplayCommandItems`; add any additional function or class method actually touched before running the audit. Constructors are checked as class members but remain undocumented per dev.md's constructor rule. Keep comments accurate for S-05 awaited movement/pathfinding, synchronous item/equipment calls, stable registry exports, and registration behavior; do not add decorator placement to command classes or user-owned state methods. Add `script/check-touched-jsdoc.ts` as a declaration-aware scanner using the TypeScript AST that enumerates top-level function declarations and every class method in the supplied files, reports the fully-qualified symbol inventory, and fails unless each inventoried declaration has multiline JSDoc. Preserve the deferred-controller/class-collapse tests and ensure every `it` call in the test file has an immediately preceding Chinese single-line coverage comment (CORR-03-14, S-04, S-05).</action>
|
||||
<action>At task start, run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-19-scope-guard.ps1" -Mode capture` exactly as defined in the scope guard. Convert every JSDoc block attached to a replay symbol touched by CORR-03-08 through CORR-03-11 into a multiline block with the description on its own line and the closing marker on its own line. Maintain this explicit touched-symbol inventory: `ReplayDirectionCommand.moveHero`, `ReplayDirectionCommand.execute`, `ReplayAutoPathfindCommand.moveToPoint`, `ReplayAutoPathfindCommand.execute`, `ReplayUseItemCommand.useItem`, `ReplayUseItemCommand.execute`, `ReplayEquipCommand.equip`, `ReplayEquipCommand.execute`, `ReplayUnequipCommand.unequip`, `ReplayUnequipCommand.execute`, `createReplayCommandItems`, and `registerReplayCommandItems`; add any additional function or class method actually touched before running the audit. The scanner must enumerate constructors but explicitly exempt `ConstructorDeclaration` nodes from the documentation requirement; every other touched top-level function and class method is required to have a multiline JSDoc block. Keep comments accurate for S-05 awaited movement/pathfinding, synchronous item/equipment calls, stable registry exports, and registration behavior; do not add decorator placement to command classes or user-owned state methods. Add `script/check-touched-jsdoc.ts` as a declaration-aware scanner using the TypeScript AST that reports the fully-qualified inventory, labels constructors as explicitly exempt, and fails for any inventoried non-constructor function or method without multiline JSDoc. Preserve the deferred-controller/class-collapse tests and ensure every `it` call in the test file has an immediately preceding Chinese single-line coverage comment (CORR-03-14, S-04, S-05).</action>
|
||||
<verify>
|
||||
<automated>pnpm exec tsx script/check-touched-jsdoc.ts packages-user/data-state/src/replay/commands.ts; if ($?) { $lines = Get-Content -LiteralPath 'packages-user/data-state/src/replay/commands.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } } }; if ($?) { pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/replay/commands.ts packages-user/data-state/src/replay/commands.test.ts script/check-touched-jsdoc.ts }</automated>
|
||||
<fails_when>The declaration-aware scan finds a touched top-level function or class method without multiline JSDoc, the replay test lacks a Chinese coverage comment, focused tests fail, or the scoped Prettier check exits non-zero.</fails_when>
|
||||
<fails_when>The declaration-aware scan finds any touched non-constructor top-level function or class method without multiline JSDoc, treats a constructor as a required-JSDoc failure instead of explicitly exempting it, the replay test lacks a Chinese coverage comment, focused tests fail, the scoped Prettier check exits non-zero, or the capture/verify scope guard is not run successfully.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- Every inventoried touched replay function and class method has multiline JSDoc and the declaration-aware scan proves that no touched method was skipped (CORR-03-14).
|
||||
- Every inventoried touched replay function and non-constructor class method has multiline JSDoc; constructors are enumerated and explicitly exempted by the declaration-aware scan, while no other touched method can be skipped (CORR-03-14).
|
||||
- Focused replay tests still prove controller ordering and one directional command ownership without changing user-owned decorator placement (CORR-03-08, CORR-03-09, CORR-03-10, S-04).
|
||||
</acceptance_criteria>
|
||||
<done>The replay correction has repository-compliant multiline function JSDoc and retains its behavioral regression path.</done>
|
||||
@ -119,13 +166,13 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
<gap_id>CORR-03-14</gap_id>
|
||||
<files>packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/event/registrations.ts, packages-user/data-state/src/event/event.test.ts</files>
|
||||
<read_first>packages-user/data-state/src/event/map.ts, packages-user/data-state/src/event/hero.ts, packages-user/data-state/src/event/event.ts, packages-user/data-state/src/event/registrations.ts, packages-user/data-state/src/event/index.ts, packages-user/data-state/src/event/event.test.ts, packages-user/data-state/src/core.ts, packages-user/data-state/src/index.ts, script/check-touched-jsdoc.ts, .planning/phases/03-data-completion/03-EVENT-CONTRACT.md, dev.md</read_first>
|
||||
<action>Convert every JSDoc block attached to an event function or registration class method touched by CORR-03-12/13 into multiline form, including the moved hero-owned front-touch function and every target-resolution, event-source collection, event-control, and registration-assembly declaration actually changed. Extend the scanner invocation to enumerate top-level functions, methods in all explicit registration classes, and any other declaration touched by the diff; do not rely on a top-level-function-only regular expression. Add multiline JSDoc where the correction creates a public event function or assembly function that requires documentation; do not reformat member-only comments in unrelated contracts or files. Keep both export-only indexes, explicit class-owned registration fields, exact eight-entry order, direct `Statement[]` behavior, front OnTouch semantics, public re-exports, and event test Chinese comments unchanged (CORR-03-14, CORR-03-12, CORR-03-13, D-11, D-24, D-27, S-03).</action>
|
||||
<action>Convert every JSDoc block attached to an event function or registration class method touched by CORR-03-12/13 into multiline form, including the moved hero-owned front-touch function and every target-resolution, event-source collection, event-control, and registration-assembly declaration actually changed. Extend the scanner invocation to enumerate top-level functions, methods in all explicit registration classes, and constructors in the same inventory; constructors are explicitly exempt from the JSDoc requirement, but every other touched function or method must have multiline JSDoc. Do not rely on a top-level-function-only regular expression. Add multiline JSDoc where the correction creates a public event function or assembly function that requires documentation; do not reformat member-only comments in unrelated contracts or files. Keep both export-only indexes, explicit class-owned registration fields, exact eight-entry order, direct `Statement[]` behavior, front OnTouch semantics, public re-exports, and event test Chinese comments unchanged (CORR-03-14, CORR-03-12, CORR-03-13, D-11, D-24, D-27, S-03).</action>
|
||||
<verify>
|
||||
<automated>pnpm exec tsx script/check-touched-jsdoc.ts packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/event/registrations.ts; if ($?) { $lines = Get-Content -LiteralPath 'packages-user/data-state/src/event/event.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } } }; if ($?) { pnpm exec vitest run packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts }; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }; if ($?) { pnpm test:data-node }; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/event/registrations.ts packages-user/data-state/src/event/event.test.ts script/check-touched-jsdoc.ts }; if ($?) { $status = & git status --porcelain=v1 --untracked-files=all; $statusExit = $LASTEXITCODE; if ($statusExit -ne 0) { throw 'git status failed during scope assertion' }; $baseline = Get-Content -LiteralPath "$env:TEMP\mota-phase03-19-scope-baseline\paths.txt"; $new = Compare-Object -ReferenceObject $baseline -DifferenceObject $status -PassThru; $allowed = @('packages-user/data-state/src/replay/commands.ts','packages-user/data-state/src/replay/commands.test.ts','packages-user/data-state/src/event/map.ts','packages-user/data-state/src/event/hero.ts','packages-user/data-state/src/event/event.ts','packages-user/data-state/src/event/registrations.ts','packages-user/data-state/src/event/event.test.ts','script/check-touched-jsdoc.ts'); foreach ($delta in $new) { if ($delta.SideIndicator -eq '=>' -and $allowed -notcontains $delta.InputObject) { throw 'Scope assertion found a newly changed path outside Plan 03-19 allowlist' } }; if (Test-Path 'packages-user/data-state/src/event/runtime.ts') { throw 'runtime.ts must not be created or used' }; git diff --check }</automated>
|
||||
<fails_when>The declaration-aware scan finds any touched event function or class method without multiline JSDoc, any event test lacks its Chinese coverage comment, event/data/Node/type/Prettier gates exit non-zero, runtime.ts is created, or the baseline-aware scope/diff assertion finds forbidden or unrelated changes.</fails_when>
|
||||
<automated>pnpm exec tsx script/check-touched-jsdoc.ts packages-user/data-state/src/replay/commands.ts packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/event/registrations.ts; if ($?) { $lines = Get-Content -LiteralPath 'packages-user/data-state/src/event/event.test.ts'; for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -match '^\s*it(?:\.[A-Za-z]+)*\s*\(') { if ($i -eq 0 -or $lines[$i - 1] -notmatch '^\s*//\s*[\u4e00-\u9fff]') { throw "Missing Chinese coverage comment before it at line $($i + 1)" } } } }; if ($?) { pnpm exec vitest run packages-user/data-state/src/replay/commands.test.ts packages-user/data-state/src/event/event.test.ts packages-user/data-system/src/event/eventDispatch.test.ts }; if ($?) { pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state }; if ($?) { pnpm test:data-node }; if ($?) { pnpm exec tsx script/check-data-type.ts }; if ($?) { pnpm exec prettier --check packages-user/data-state/src/replay/commands.ts packages-user/data-state/src/replay/commands.test.ts packages-user/data-state/src/event/map.ts packages-user/data-state/src/event/hero.ts packages-user/data-state/src/event/event.ts packages-user/data-state/src/event/registrations.ts packages-user/data-state/src/event/event.test.ts script/check-touched-jsdoc.ts }; if ($?) { powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-19-scope-guard.ps1" -Mode verify }</automated>
|
||||
<fails_when>The declaration-aware scan finds any touched non-constructor event function or class method without multiline JSDoc, reports a constructor as a documentation failure rather than an explicit exemption, any event test lacks its Chinese coverage comment, event/data/Node/type/Prettier gates exit non-zero, runtime.ts is created, or `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-19-scope-guard.ps1" -Mode verify` finds a new non-allowlisted path, changed baseline bytes/hunks, changed forbidden legacy/save/attribute diff, decorator-placement hunk, or unrelated formatting.</fails_when>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- Event function and registration class-method JSDoc is multiline for every declaration enumerated by the scanner; unrelated legacy/save/render files are not reformatted (CORR-03-14).
|
||||
- Event function and non-constructor registration class-method JSDoc is multiline for every declaration enumerated by the scanner; constructors are explicitly exempted, and unrelated legacy/save/render files are not reformatted (CORR-03-14).
|
||||
- Event ownership, both index export-only shapes, hero front-touch placement, replay await behavior under S-05, direct `Statement[]` insertion, stable order, public exports, and all data gates remain green (CORR-03-12, CORR-03-13, D-04, D-15, S-01, S-03, S-04, S-05).
|
||||
</acceptance_criteria>
|
||||
<done>All touched replay/event function JSDoc and test-comment requirements pass without runtime or out-of-scope file changes.</done>
|
||||
@ -157,13 +204,13 @@ Before the first edit, snapshot the current `git status --porcelain=v1 --untrack
|
||||
- `pnpm test:ci packages-user/data-common packages-user/data-base packages-user/data-system packages-user/data-state`
|
||||
- `pnpm test:data-node`
|
||||
- `pnpm exec tsx script/check-data-type.ts`
|
||||
- `pnpm exec tsx script/check-touched-jsdoc.ts` over replay commands and all event implementation/registration files; the declaration-aware scan must cover class methods as well as top-level functions.
|
||||
- `pnpm exec tsx script/check-touched-jsdoc.ts` over replay commands and all event implementation/registration files; the declaration-aware scan must inventory constructors as explicit exemptions, then require multiline JSDoc for every other class method and top-level function.
|
||||
- Scoped ESLint and Prettier checks for the correction-owned replay/event files and the scanner
|
||||
- Baseline-aware mechanical diff/scope assertion from `<scope_guard>`; it must reject newly changed legacy/save paths, `attribute.ts`/decorator placement, runtime.ts, and unrelated formatting while preserving pre-existing working-tree changes.
|
||||
- Run `powershell -NoProfile -ExecutionPolicy Bypass -File "$env:TEMP\mota-phase03-19-scope-guard.ps1" -Mode verify`; its normalized path/diff baseline explicitly compares saved per-file bytes and hunks for `core.ts` and existing save/test files, the forbidden-path patch, and baseline-to-current deltas while rejecting runtime.ts, decorator placement, or unrelated formatting and preserving pre-existing working-tree changes.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
CORR-03-14 is closed when every touched top-level function and class method identified by the explicit inventory/declaration-aware scan is multiline-documented, all changed tests pass the Chinese `it` comment audit, and corrected S-05 replay/event behavior and data gates remain green without legacy/save, runtime.ts, public-contract, or user-owned decorator changes.
|
||||
CORR-03-14 is closed when every touched top-level function and non-constructor class method identified by the explicit inventory/declaration-aware scan is multiline-documented, constructors are explicitly exempted, all changed tests pass the Chinese `it` comment audit, and corrected S-05 replay/event behavior and data gates remain green without legacy/save, runtime.ts, public-contract, or user-owned decorator changes.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user