its-magic 0.1.2-9 → 0.1.3-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1602 -755
- package/bin/its-magic.js +121 -11
- package/bin/postinstall.js +93 -0
- package/installer.ps1 +759 -0
- package/installer.py +1036 -0
- package/installer.sh +649 -0
- package/package.json +18 -14
- package/scripts/check_intake_template_parity.py +312 -0
- package/scripts/doc_profile_lib.py +415 -0
- package/scripts/guard_installer_publish.py +88 -0
- package/scripts/intake_bug_routing_guard.py +67 -0
- package/scripts/intake_evidence_lib.py +802 -0
- package/scripts/intake_evidence_validate.py +73 -0
- package/scripts/materialize_codebase_map.py +184 -0
- package/scripts/remote_config_summary.py +243 -0
- package/template/.cursor/agents/curator.mdc +35 -0
- package/template/.cursor/agents/dev.mdc +29 -0
- package/template/.cursor/agents/po.mdc +141 -0
- package/template/.cursor/agents/qa.mdc +28 -0
- package/template/.cursor/agents/release.mdc +28 -0
- package/template/.cursor/agents/security.mdc +98 -0
- package/template/.cursor/agents/tech-lead.mdc +57 -0
- package/template/.cursor/commands/architecture.md +127 -0
- package/template/.cursor/commands/ask.md +55 -0
- package/template/.cursor/commands/auto.md +533 -0
- package/template/.cursor/commands/discovery.md +47 -0
- package/template/.cursor/commands/execute.md +343 -0
- package/template/.cursor/commands/intake.md +323 -0
- package/template/.cursor/commands/map-codebase.md +46 -0
- package/template/.cursor/commands/memory-audit.md +175 -0
- package/template/.cursor/commands/milestone-complete.md +51 -0
- package/template/.cursor/commands/milestone-start.md +59 -0
- package/template/.cursor/commands/pause.md +64 -0
- package/{.cursor/commands/gsd-phase-context.md → template/.cursor/commands/phase-context.md} +6 -2
- package/template/.cursor/commands/plan-verify.md +34 -0
- package/template/.cursor/commands/qa.md +226 -0
- package/template/.cursor/commands/quick.md +41 -0
- package/template/.cursor/commands/refresh-context.md +82 -0
- package/template/.cursor/commands/release.md +523 -0
- package/template/.cursor/commands/research.md +49 -0
- package/template/.cursor/commands/resume.md +67 -0
- package/template/.cursor/commands/security-review.md +81 -0
- package/template/.cursor/commands/sprint-plan.md +103 -0
- package/template/.cursor/commands/status-reconcile.md +95 -0
- package/template/.cursor/commands/verify-work.md +152 -0
- package/template/.cursor/dev-environment.json.example +22 -0
- package/{.cursor → template/.cursor}/hooks/README.md +3 -2
- package/{.cursor/hooks/gsd-hook.py → template/.cursor/hooks/hook.py} +15 -6
- package/template/.cursor/hooks.json +26 -0
- package/template/.cursor/remote.json +31 -0
- package/template/.cursor/rules/caveman.mdc +184 -0
- package/template/.cursor/rules/coding-standards.mdc +39 -0
- package/template/.cursor/rules/core.mdc +111 -0
- package/template/.cursor/rules/handoffs.mdc +27 -0
- package/template/.cursor/rules/quality.mdc +49 -0
- package/template/.cursor/scratchpad.local.example.md +323 -0
- package/template/.cursor/scratchpad.md +324 -0
- package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/SKILL.md +2 -2
- package/template/.cursorignore +6 -0
- package/template/.env.example +28 -0
- package/template/.github/workflows/ci.yml +194 -0
- package/{.github → template/.github}/workflows/deploy.yml +5 -2
- package/template/.its-magic-version +1 -0
- package/template/CHANGELOG.md +11 -0
- package/template/README.md +1602 -0
- package/template/decisions/DEC-0001.md +11 -0
- package/template/decisions/DEC-0002.md +11 -0
- package/template/docs/developer/README.md +44 -0
- package/template/docs/engineering/architecture.md +9 -0
- package/template/docs/engineering/artifact-ordering-policy.md +48 -0
- package/template/docs/engineering/artifact-ownership-policy.md +57 -0
- package/template/docs/engineering/auto-orchestration-reference.md +1211 -0
- package/{docs → template/docs}/engineering/codebase-map.md +0 -5
- package/template/docs/engineering/compatibility-report.md +20 -0
- package/template/docs/engineering/compatibility-signals.md +7 -0
- package/template/docs/engineering/component-scope-report.md +16 -0
- package/template/docs/engineering/component-scope.md +19 -0
- package/template/docs/engineering/context/installer-owned-paths.manifest +96 -0
- package/template/docs/engineering/context/readme-section-affinity.json +30 -0
- package/template/docs/engineering/decisions.md +16 -0
- package/template/docs/engineering/legacy-drift-audit.md +21 -0
- package/template/docs/engineering/manifests/components/api-gateway.manifest.yaml +12 -0
- package/template/docs/engineering/manifests/registry.manifest.yaml +20 -0
- package/template/docs/engineering/manifests/repo.manifest.yaml +11 -0
- package/template/docs/engineering/phase-context.md +16 -0
- package/template/docs/engineering/release-targets.json +123 -0
- package/template/docs/engineering/research.md +29 -0
- package/template/docs/engineering/runbook.md +2391 -0
- package/template/docs/engineering/runtime-connectivity.md +81 -0
- package/template/docs/engineering/security-review.md +33 -0
- package/template/docs/engineering/spec-pack/README.md +20 -0
- package/template/docs/engineering/state-archive/README.md +15 -0
- package/template/docs/engineering/state.md +20 -0
- package/template/docs/engineering/status-normalization-report.md +19 -0
- package/template/docs/engineering/token-cost-parity-manifest.md +16 -0
- package/template/docs/engineering/us-0084-remote-e2e.md +44 -0
- package/template/docs/product/acceptance.md +1 -0
- package/template/docs/product/backlog.md +5 -0
- package/template/docs/product/vision.md +11 -0
- package/template/docs/user-guides/README.md +40 -0
- package/{handoffs → template/handoffs}/dev_to_qa.md +0 -3
- package/{handoffs → template/handoffs}/po_to_tl.md +0 -3
- package/{handoffs → template/handoffs}/qa_to_dev.md +1 -2
- package/template/handoffs/release_notes.md +51 -0
- package/template/handoffs/release_queue.md +47 -0
- package/template/handoffs/release_to_dev.md +31 -0
- package/template/handoffs/releases/Sxxxx-release-notes.md +62 -0
- package/template/handoffs/releases/vX.Y.Z-release-notes.md.example +21 -0
- package/template/handoffs/resume_brief.md +14 -0
- package/{handoffs → template/handoffs}/tl_to_dev.md +0 -2
- package/template/handoffs/token_cost_runs/README.md +26 -0
- package/template/its_magic/.its-magic-version +1 -0
- package/template/its_magic/README.md +9 -0
- package/template/scripts/auto_outer_driver.py +521 -0
- package/template/scripts/caveman_compress_input.py +903 -0
- package/template/scripts/check_downstream_ci_guard.py +67 -0
- package/template/scripts/check_intake_template_parity.py +312 -0
- package/template/scripts/check_token_cost_parity.py +69 -0
- package/template/scripts/dev_environment_lib.py +601 -0
- package/template/scripts/doc_profile_lib.py +415 -0
- package/template/scripts/downstream_ci_guard_lib.py +222 -0
- package/template/scripts/enforce-triad-hot-surface.py +753 -0
- package/template/scripts/guard_installer_publish.py +88 -0
- package/template/scripts/intake_bug_resume_brief_refresh.py +303 -0
- package/template/scripts/intake_bug_routing_guard.py +67 -0
- package/template/scripts/intake_evidence_lib.py +802 -0
- package/template/scripts/intake_evidence_validate.py +73 -0
- package/template/scripts/materialize_codebase_map.py +184 -0
- package/template/scripts/pack_json_validate.py +130 -0
- package/template/scripts/project_readme_coverage_lib.py +417 -0
- package/template/scripts/readme_feature_coverage_lib.py +608 -0
- package/{scripts → template/scripts}/release-all.sh +27 -4
- package/template/scripts/release_changelog_backfill.py +153 -0
- package/template/scripts/release_changelog_lib.py +544 -0
- package/template/scripts/release_changelog_validate.py +134 -0
- package/template/scripts/remote_config_summary.py +243 -0
- package/template/scripts/sync_push_gates.py +198 -0
- package/template/scripts/token_cost_compare.py +40 -0
- package/template/scripts/token_cost_lib.py +108 -0
- package/template/scripts/uat_probe_lib.py +868 -0
- package/template/scripts/validate-and-push.ps1 +280 -0
- package/template/scripts/validate-and-push.sh +243 -0
- package/template/scripts/validate_doc_profile.py +103 -0
- package/template/scripts/validate_project_readme_coverage.py +151 -0
- package/template/scripts/validate_readme_feature_coverage.py +140 -0
- package/template/sprints/S0001/progress.md +1 -0
- package/template/sprints/S0001/qa-findings.md +9 -0
- package/template/sprints/S0001/release-findings.md +24 -0
- package/template/sprints/S0001/sprint.md +9 -0
- package/template/sprints/S0001/summary.md +7 -0
- package/template/sprints/S0001/tasks.md +1 -0
- package/template/sprints/S0001/uat.json +6 -0
- package/template/sprints/S0001/uat.md +5 -0
- package/template/sprints/quick/Q0001/summary.md +1 -0
- package/{sprints → template/sprints}/quick/Q0001/task.json +0 -1
- package/.cursor/agents/curator.mdc +0 -21
- package/.cursor/agents/dev.mdc +0 -20
- package/.cursor/agents/po.mdc +0 -19
- package/.cursor/agents/qa.mdc +0 -19
- package/.cursor/agents/release.mdc +0 -19
- package/.cursor/agents/tech-lead.mdc +0 -21
- package/.cursor/commands/gsd-architecture.md +0 -29
- package/.cursor/commands/gsd-auto.md +0 -27
- package/.cursor/commands/gsd-discovery.md +0 -27
- package/.cursor/commands/gsd-execute.md +0 -32
- package/.cursor/commands/gsd-intake.md +0 -28
- package/.cursor/commands/gsd-map-codebase.md +0 -25
- package/.cursor/commands/gsd-milestone-complete.md +0 -24
- package/.cursor/commands/gsd-milestone-start.md +0 -26
- package/.cursor/commands/gsd-pause.md +0 -25
- package/.cursor/commands/gsd-plan-verify.md +0 -26
- package/.cursor/commands/gsd-qa.md +0 -28
- package/.cursor/commands/gsd-quick.md +0 -24
- package/.cursor/commands/gsd-refresh-context.md +0 -26
- package/.cursor/commands/gsd-release.md +0 -29
- package/.cursor/commands/gsd-research.md +0 -28
- package/.cursor/commands/gsd-resume.md +0 -26
- package/.cursor/commands/gsd-sprint-plan.md +0 -30
- package/.cursor/commands/gsd-verify-work.md +0 -25
- package/.cursor/hooks.json +0 -26
- package/.cursor/plans/cursor-gsd-team-kit_8cfee9b8.plan.md +0 -57
- package/.cursor/remote.json +0 -18
- package/.cursor/rules/gsd-core.mdc +0 -18
- package/.cursor/rules/gsd-handoffs.mdc +0 -10
- package/.cursor/rules/gsd-quality.mdc +0 -15
- package/.cursor/scratchpad.md +0 -34
- package/.github/workflows/ci.yml +0 -47
- package/decisions/DEC-0001.md +0 -21
- package/decisions/DEC-0002.md +0 -21
- package/docs/engineering/architecture.md +0 -354
- package/docs/engineering/decisions.md +0 -6
- package/docs/engineering/research.md +0 -11
- package/docs/engineering/runbook.md +0 -32
- package/docs/engineering/state.md +0 -33
- package/docs/product/acceptance.md +0 -6
- package/docs/product/backlog.md +0 -7
- package/docs/product/vision.md +0 -46
- package/gsd-installer.ps1 +0 -189
- package/gsd-installer.py +0 -195
- package/gsd-installer.sh +0 -201
- package/handoffs/release_notes.md +0 -14
- package/handoffs/resume_brief.md +0 -8
- package/milestones/M0001/milestone.json +0 -7
- package/milestones/M0001/phases.json +0 -9
- package/milestones/M0001/progress.md +0 -3
- package/milestones/M0001/summary.md +0 -3
- package/scripts/generate-release-notes.ps1 +0 -74
- package/scripts/generate-release-notes.sh +0 -63
- package/scripts/release-all.ps1 +0 -423
- package/sprints/S0001/progress.md +0 -4
- package/sprints/S0001/qa-findings.md +0 -113
- package/sprints/S0001/sprint.md +0 -70
- package/sprints/S0001/summary.md +0 -46
- package/sprints/S0001/tasks.md +0 -35
- package/sprints/S0001/uat.json +0 -8
- package/sprints/S0001/uat.md +0 -8
- package/sprints/quick/Q0001/summary.md +0 -3
- /package/{.cursor/rules/gsd-escalation.mdc → template/.cursor/rules/escalation.mdc} +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/acceptance.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/architecture.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/decision.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/handoff.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/milestone.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/phase-context.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/plan-verify.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/sprint.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.json +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/story.md +0 -0
- /package/{.cursor/skills/gsd-team → template/.cursor/skills/its-magic}/templates/uat.json +0 -0
- /package/{docs → template/docs}/engineering/context/phase-template.json +0 -0
- /package/{docs → template/docs}/engineering/dependencies.json +0 -0
- /package/{sprints → template/sprints}/S0001/plan-verify.json +0 -0
package/scripts/release-all.ps1
DELETED
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
<#
|
|
2
|
-
.SYNOPSIS
|
|
3
|
-
Unified release: npm + Chocolatey + Homebrew (all three at once).
|
|
4
|
-
.DESCRIPTION
|
|
5
|
-
1. Bumps version in package.json (patch|minor|major|explicit)
|
|
6
|
-
2. Publishes to npm
|
|
7
|
-
3. Creates a GitHub release with a source zip (for Homebrew/Chocolatey URLs)
|
|
8
|
-
4. Updates Chocolatey nuspec + pushes to chocolatey.org
|
|
9
|
-
5. Updates Homebrew formula with new URL + sha256
|
|
10
|
-
.PARAMETER Bump
|
|
11
|
-
Version bump type: patch, minor, major, or an explicit semver (e.g. 1.2.3).
|
|
12
|
-
Default: patch
|
|
13
|
-
.PARAMETER NpmTag
|
|
14
|
-
npm dist-tag (e.g. latest, beta, rc). Default: latest
|
|
15
|
-
.PARAMETER SkipNpm
|
|
16
|
-
Skip npm publish.
|
|
17
|
-
.PARAMETER SkipChoco
|
|
18
|
-
Skip Chocolatey push.
|
|
19
|
-
.PARAMETER SkipBrew
|
|
20
|
-
Skip Homebrew formula update.
|
|
21
|
-
.PARAMETER DryRun
|
|
22
|
-
Print what would happen without executing.
|
|
23
|
-
#>
|
|
24
|
-
param(
|
|
25
|
-
[string]$Bump = "patch",
|
|
26
|
-
[string]$NpmTag = "latest",
|
|
27
|
-
[switch]$SkipNpm,
|
|
28
|
-
[switch]$SkipChoco,
|
|
29
|
-
[switch]$SkipBrew,
|
|
30
|
-
[switch]$DryRun,
|
|
31
|
-
[switch]$SkipBrewPush,
|
|
32
|
-
[string]$BrewTapRepo = "",
|
|
33
|
-
[string]$BrewTapBranch = "main",
|
|
34
|
-
[string]$BrewTapDir = "",
|
|
35
|
-
[bool]$CreateBrewTapIfMissing = $true
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
$ErrorActionPreference = 'Stop'
|
|
39
|
-
$repoRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
|
40
|
-
if (-not (Test-Path (Join-Path $repoRoot 'package.json'))) {
|
|
41
|
-
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
42
|
-
}
|
|
43
|
-
Push-Location $repoRoot
|
|
44
|
-
|
|
45
|
-
# ── Helpers ──────────────────────────────────────────────────────────
|
|
46
|
-
function Log($msg) { Write-Host "[release] $msg" -ForegroundColor Cyan }
|
|
47
|
-
function Warn($msg){ Write-Host "[release] $msg" -ForegroundColor Yellow }
|
|
48
|
-
function Err($msg) { Write-Host "[release] $msg" -ForegroundColor Red; Pop-Location; exit 1 }
|
|
49
|
-
|
|
50
|
-
# Quote char for building regex patterns (avoids PS 5.1 parser issues)
|
|
51
|
-
$DQ = [char]34
|
|
52
|
-
$chocoFailed = $false
|
|
53
|
-
$brewTapFailed = $false
|
|
54
|
-
$brewTapPushed = $false
|
|
55
|
-
|
|
56
|
-
# ── 1. Version bump ─────────────────────────────────────────────────
|
|
57
|
-
Log "Bumping version ($Bump) ..."
|
|
58
|
-
if ($DryRun) {
|
|
59
|
-
Log "(dry-run) would run: npm version $Bump --no-git-tag-version"
|
|
60
|
-
$newVersion = "0.0.0-dryrun"
|
|
61
|
-
} else {
|
|
62
|
-
npm version $Bump --no-git-tag-version | Out-Null
|
|
63
|
-
$pkg = Get-Content package.json -Raw | ConvertFrom-Json
|
|
64
|
-
$newVersion = $pkg.version
|
|
65
|
-
}
|
|
66
|
-
Log "New version: $newVersion"
|
|
67
|
-
|
|
68
|
-
# ── 2. npm publish ───────────────────────────────────────────────────
|
|
69
|
-
if (-not $SkipNpm) {
|
|
70
|
-
Log "Publishing to npm (tag=$NpmTag) ..."
|
|
71
|
-
if ($DryRun) {
|
|
72
|
-
Log "(dry-run) would run: npm publish --tag $NpmTag"
|
|
73
|
-
} else {
|
|
74
|
-
npm publish --tag $NpmTag
|
|
75
|
-
if ($LASTEXITCODE -ne 0) { Err "npm publish failed" }
|
|
76
|
-
Log "npm publish OK"
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
Warn "Skipping npm"
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
# ── 3. GitHub release (needed by Homebrew + Chocolatey) ──────────────
|
|
83
|
-
$ghAvailable = $null -ne (Get-Command gh -ErrorAction SilentlyContinue)
|
|
84
|
-
$tagName = "v$newVersion"
|
|
85
|
-
$zipUrl = ""
|
|
86
|
-
$tarUrl = ""
|
|
87
|
-
|
|
88
|
-
$isPrerelease = $newVersion -match '-'
|
|
89
|
-
$chocoVersion = $newVersion
|
|
90
|
-
if ($newVersion -match '^(\d+\.\d+\.\d+)-(\d+)$') {
|
|
91
|
-
# Old Chocolatey/NuGet rejects numeric-only prerelease labels (e.g. 0.1.1-1).
|
|
92
|
-
# Convert to a compatible label for nuspec only.
|
|
93
|
-
$chocoVersion = "$($Matches[1])-beta$($Matches[2])"
|
|
94
|
-
}
|
|
95
|
-
if ($chocoVersion -ne $newVersion) {
|
|
96
|
-
Log "Chocolatey version normalized: $newVersion -> $chocoVersion"
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if ($ghAvailable) {
|
|
100
|
-
Log "Creating GitHub release $tagName ..."
|
|
101
|
-
if ($DryRun) {
|
|
102
|
-
Log "(dry-run) would run: gh release create $tagName --generate-notes"
|
|
103
|
-
$zipUrl = "https://github.com/USER/its-magic/archive/refs/tags/$tagName.zip"
|
|
104
|
-
$tarUrl = "https://github.com/USER/its-magic/archive/refs/tags/$tagName.tar.gz"
|
|
105
|
-
} else {
|
|
106
|
-
$ghArgs = @("release", "create", $tagName, "--generate-notes", "--title", $tagName)
|
|
107
|
-
if ($isPrerelease) { $ghArgs += "--prerelease" }
|
|
108
|
-
& gh @ghArgs
|
|
109
|
-
if ($LASTEXITCODE -ne 0) { Warn "gh release create failed - continuing anyway" }
|
|
110
|
-
# Derive archive URL from current remote
|
|
111
|
-
$remoteUrl = git remote get-url origin 2>$null
|
|
112
|
-
if ($remoteUrl -match 'github\.com[:/](.+?)(\.git)?$') {
|
|
113
|
-
$repoSlug = $Matches[1]
|
|
114
|
-
$zipUrl = "https://github.com/$repoSlug/archive/refs/tags/$tagName.zip"
|
|
115
|
-
$tarUrl = "https://github.com/$repoSlug/archive/refs/tags/$tagName.tar.gz"
|
|
116
|
-
}
|
|
117
|
-
Log "GitHub release created: $tagName"
|
|
118
|
-
}
|
|
119
|
-
} else {
|
|
120
|
-
Warn "gh CLI not found - skipping GitHub release"
|
|
121
|
-
$zipUrl = "https://github.com/USER/its-magic/archive/refs/tags/$tagName.zip"
|
|
122
|
-
$tarUrl = "https://github.com/USER/its-magic/archive/refs/tags/$tagName.tar.gz"
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
# ── 4. Chocolatey ───────────────────────────────────────────────────
|
|
126
|
-
if (-not $SkipChoco) {
|
|
127
|
-
$chocoDir = Join-Path $repoRoot 'packaging\chocolatey'
|
|
128
|
-
$nuspec = Join-Path $chocoDir 'its-magic.nuspec'
|
|
129
|
-
$chocoInstall = Join-Path $chocoDir 'tools\chocolateyInstall.ps1'
|
|
130
|
-
|
|
131
|
-
if (Test-Path $nuspec) {
|
|
132
|
-
Log "Updating Chocolatey nuspec to $chocoVersion ..."
|
|
133
|
-
if (-not $DryRun) {
|
|
134
|
-
# Update version in nuspec
|
|
135
|
-
$xml = [xml](Get-Content $nuspec -Raw)
|
|
136
|
-
$xml.package.metadata.version = $chocoVersion
|
|
137
|
-
$xml.Save($nuspec)
|
|
138
|
-
|
|
139
|
-
# Update URL in install script
|
|
140
|
-
$installContent = Get-Content $chocoInstall -Raw
|
|
141
|
-
if ($zipUrl) {
|
|
142
|
-
$installContent = $installContent -replace "\`$url\s*=\s*'[^']+'", "`$url = '$zipUrl'"
|
|
143
|
-
}
|
|
144
|
-
Set-Content -Path $chocoInstall -Value $installContent
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
# Compute checksum if we have the zip
|
|
148
|
-
if ($zipUrl -and -not $DryRun) {
|
|
149
|
-
Log "Downloading zip for checksum ..."
|
|
150
|
-
$tmpZip = Join-Path $env:TEMP "gsd-$tagName.zip"
|
|
151
|
-
try {
|
|
152
|
-
Invoke-WebRequest -Uri $zipUrl -OutFile $tmpZip -UseBasicParsing
|
|
153
|
-
$sha = (Get-FileHash -Path $tmpZip -Algorithm SHA256).Hash.ToLower()
|
|
154
|
-
$installContent = Get-Content $chocoInstall -Raw
|
|
155
|
-
$installContent = $installContent -replace "\`$checksum\s*=\s*'[^']+'", "`$checksum = '$sha'"
|
|
156
|
-
Set-Content -Path $chocoInstall -Value $installContent
|
|
157
|
-
Remove-Item $tmpZip -Force
|
|
158
|
-
Log "Checksum: $sha"
|
|
159
|
-
} catch {
|
|
160
|
-
Warn "Could not download zip for checksum - set PLACEHOLDER manually"
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
$chocoAvailable = $null -ne (Get-Command choco -ErrorAction SilentlyContinue)
|
|
165
|
-
if ($chocoAvailable) {
|
|
166
|
-
Log "Packing + pushing Chocolatey package ..."
|
|
167
|
-
if ($DryRun) {
|
|
168
|
-
Log "(dry-run) would run: choco pack + choco push"
|
|
169
|
-
} else {
|
|
170
|
-
Push-Location $chocoDir
|
|
171
|
-
choco pack
|
|
172
|
-
if ($LASTEXITCODE -ne 0) {
|
|
173
|
-
$chocoFailed = $true
|
|
174
|
-
Warn "choco pack failed"
|
|
175
|
-
} else {
|
|
176
|
-
$nupkg = Get-ChildItem "*.nupkg" | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
|
177
|
-
if ($nupkg) {
|
|
178
|
-
choco push $nupkg.Name --source https://push.chocolatey.org/
|
|
179
|
-
if ($LASTEXITCODE -ne 0) {
|
|
180
|
-
$chocoFailed = $true
|
|
181
|
-
Warn "choco push failed"
|
|
182
|
-
}
|
|
183
|
-
else { Log "Chocolatey push OK" }
|
|
184
|
-
} else {
|
|
185
|
-
$chocoFailed = $true
|
|
186
|
-
Warn "No .nupkg created by choco pack"
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
Pop-Location
|
|
190
|
-
}
|
|
191
|
-
} else {
|
|
192
|
-
$chocoFailed = $true
|
|
193
|
-
Warn "choco not found - nuspec updated but not pushed. Run choco pack and choco push manually."
|
|
194
|
-
}
|
|
195
|
-
} else {
|
|
196
|
-
$chocoFailed = $true
|
|
197
|
-
Warn "nuspec not found at $nuspec - skipping Chocolatey"
|
|
198
|
-
}
|
|
199
|
-
} else {
|
|
200
|
-
Warn "Skipping Chocolatey"
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
# ── 5. Homebrew ─────────────────────────────────────────────────────
|
|
204
|
-
if (-not $SkipBrew) {
|
|
205
|
-
# Detect pre-release: anything with a hyphen
|
|
206
|
-
if ($isPrerelease) {
|
|
207
|
-
$formulaPath = Join-Path $repoRoot 'packaging\homebrew\its-magic-beta.rb'
|
|
208
|
-
Log "Pre-release detected - using beta formula"
|
|
209
|
-
} else {
|
|
210
|
-
$formulaPath = Join-Path $repoRoot 'packaging\homebrew\its-magic.rb'
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (Test-Path $formulaPath) {
|
|
214
|
-
$formulaName = Split-Path -Leaf $formulaPath
|
|
215
|
-
Log "Updating Homebrew formula ($formulaName) to $newVersion ..."
|
|
216
|
-
if (-not $DryRun) {
|
|
217
|
-
$formula = Get-Content $formulaPath -Raw
|
|
218
|
-
|
|
219
|
-
# Update version in URL
|
|
220
|
-
if ($tarUrl) {
|
|
221
|
-
$urlPattern = 'url ' + $DQ + 'https://github\.com/[^' + $DQ + ']+\.tar\.gz' + $DQ
|
|
222
|
-
$urlReplace = 'url ' + $DQ + $tarUrl + $DQ
|
|
223
|
-
$formula = $formula -replace $urlPattern, $urlReplace
|
|
224
|
-
} else {
|
|
225
|
-
$formula = $formula -replace 'vVERSION', $tagName
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
# Update explicit version line for beta formula
|
|
229
|
-
if ($isPrerelease) {
|
|
230
|
-
$verPattern = 'version ' + $DQ + '[^' + $DQ + ']*' + $DQ
|
|
231
|
-
$verReplace = 'version ' + $DQ + $newVersion + $DQ
|
|
232
|
-
$formula = $formula -replace $verPattern, $verReplace
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
# Compute tar.gz sha256 if possible
|
|
236
|
-
if ($tarUrl) {
|
|
237
|
-
$tmpTar = Join-Path $env:TEMP "gsd-$tagName.tar.gz"
|
|
238
|
-
try {
|
|
239
|
-
Invoke-WebRequest -Uri $tarUrl -OutFile $tmpTar -UseBasicParsing
|
|
240
|
-
$sha = (Get-FileHash -Path $tmpTar -Algorithm SHA256).Hash.ToLower()
|
|
241
|
-
$shaPattern = 'sha256 ' + $DQ + '[^' + $DQ + ']*' + $DQ
|
|
242
|
-
$shaReplace = 'sha256 ' + $DQ + $sha + $DQ
|
|
243
|
-
$formula = $formula -replace $shaPattern, $shaReplace
|
|
244
|
-
Remove-Item $tmpTar -Force
|
|
245
|
-
Log "Homebrew sha256: $sha"
|
|
246
|
-
} catch {
|
|
247
|
-
Warn "Could not download tar.gz - set sha256 manually in the formula"
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
Set-Content -Path $formulaPath -Value $formula
|
|
252
|
-
Log "Homebrew formula updated: $formulaName"
|
|
253
|
-
if ($isPrerelease) {
|
|
254
|
-
Log "Users install beta with: brew install USER/tap/its-magic-beta"
|
|
255
|
-
} else {
|
|
256
|
-
Log "Users install stable with: brew install USER/tap/its-magic"
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
$formulaName = Split-Path -Leaf $formulaPath
|
|
260
|
-
Log "(dry-run) would update $formulaName"
|
|
261
|
-
}
|
|
262
|
-
} else {
|
|
263
|
-
Warn "Formula not found at $formulaPath - skipping Homebrew"
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
# Optional: publish formulas to Homebrew tap repository
|
|
267
|
-
if (-not $SkipBrewPush) {
|
|
268
|
-
if ($DryRun) {
|
|
269
|
-
Log "(dry-run) would publish formulas to Homebrew tap"
|
|
270
|
-
} else {
|
|
271
|
-
$gitAvailable = $null -ne (Get-Command git -ErrorAction SilentlyContinue)
|
|
272
|
-
$ghAvailableForTap = $null -ne (Get-Command gh -ErrorAction SilentlyContinue)
|
|
273
|
-
if (-not $gitAvailable) {
|
|
274
|
-
$brewTapFailed = $true
|
|
275
|
-
Warn "git not found - cannot push Homebrew formulas to tap"
|
|
276
|
-
} else {
|
|
277
|
-
if (-not $BrewTapRepo) {
|
|
278
|
-
$originUrl = git remote get-url origin 2>$null
|
|
279
|
-
if ($originUrl -match 'github\.com[:/](.+?)/(.+?)(\.git)?$') {
|
|
280
|
-
$owner = $Matches[1]
|
|
281
|
-
$BrewTapRepo = "$owner/homebrew-tap"
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
if (-not $BrewTapRepo) {
|
|
286
|
-
$brewTapFailed = $true
|
|
287
|
-
Warn "Could not infer Homebrew tap repo. Pass -BrewTapRepo owner/homebrew-tap"
|
|
288
|
-
} else {
|
|
289
|
-
if (-not $BrewTapDir) {
|
|
290
|
-
$safeTap = ($BrewTapRepo -replace '[^a-zA-Z0-9_-]', '-')
|
|
291
|
-
$BrewTapDir = Join-Path $env:TEMP "its-magic-$safeTap"
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
Log "Publishing formulas to tap $BrewTapRepo (branch: $BrewTapBranch) ..."
|
|
295
|
-
|
|
296
|
-
# Use a child scope with relaxed error handling for git commands
|
|
297
|
-
# that write to stderr even on success (e.g. empty repo warnings).
|
|
298
|
-
$tapReady = $true
|
|
299
|
-
$prevEAP = $ErrorActionPreference
|
|
300
|
-
$ErrorActionPreference = 'Continue'
|
|
301
|
-
|
|
302
|
-
# ── Step A: Ensure we have a local clone ──
|
|
303
|
-
$tapGitDir = Join-Path $BrewTapDir ".git"
|
|
304
|
-
|
|
305
|
-
# Clean up non-git directory leftovers
|
|
306
|
-
if ((Test-Path $BrewTapDir) -and -not (Test-Path $tapGitDir)) {
|
|
307
|
-
Warn "Tap directory exists but is not a git repo. Removing: $BrewTapDir"
|
|
308
|
-
Remove-Item -Recurse -Force $BrewTapDir
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
if (Test-Path $tapGitDir) {
|
|
312
|
-
# Already cloned - fetch latest
|
|
313
|
-
git -C $BrewTapDir fetch origin 2>&1 | Out-Null
|
|
314
|
-
} else {
|
|
315
|
-
# Clone (try gh first, then git)
|
|
316
|
-
$cloneOk = $false
|
|
317
|
-
if ($ghAvailableForTap) {
|
|
318
|
-
& gh repo clone $BrewTapRepo $BrewTapDir 2>&1 | Out-Null
|
|
319
|
-
if ($LASTEXITCODE -eq 0) { $cloneOk = $true }
|
|
320
|
-
}
|
|
321
|
-
if (-not $cloneOk) {
|
|
322
|
-
git clone "https://github.com/$BrewTapRepo.git" $BrewTapDir 2>&1 | Out-Null
|
|
323
|
-
if ($LASTEXITCODE -eq 0) { $cloneOk = $true }
|
|
324
|
-
}
|
|
325
|
-
if (-not $cloneOk -and $CreateBrewTapIfMissing -and $ghAvailableForTap) {
|
|
326
|
-
Warn "Tap repo not found. Creating $BrewTapRepo on GitHub ..."
|
|
327
|
-
& gh repo create $BrewTapRepo --public --description "Homebrew tap for its-magic" 2>&1 | Out-Null
|
|
328
|
-
if ($LASTEXITCODE -eq 0) {
|
|
329
|
-
Log "Tap repo created: https://github.com/$BrewTapRepo"
|
|
330
|
-
if (Test-Path $BrewTapDir) { Remove-Item -Recurse -Force $BrewTapDir }
|
|
331
|
-
& gh repo clone $BrewTapRepo $BrewTapDir 2>&1 | Out-Null
|
|
332
|
-
if ($LASTEXITCODE -eq 0) { $cloneOk = $true }
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (-not $cloneOk) {
|
|
336
|
-
$tapReady = $false; $brewTapFailed = $true
|
|
337
|
-
Warn "Failed to clone/create tap repo $BrewTapRepo"
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
# ── Step B: Ensure branch exists ──
|
|
342
|
-
if ($tapReady) {
|
|
343
|
-
# Check if repo has any commits at all
|
|
344
|
-
$hasCommits = $false
|
|
345
|
-
git -C $BrewTapDir log --oneline -1 2>&1 | Out-Null
|
|
346
|
-
if ($LASTEXITCODE -eq 0) { $hasCommits = $true }
|
|
347
|
-
|
|
348
|
-
if ($hasCommits) {
|
|
349
|
-
git -C $BrewTapDir checkout $BrewTapBranch 2>&1 | Out-Null
|
|
350
|
-
if ($LASTEXITCODE -ne 0) {
|
|
351
|
-
git -C $BrewTapDir checkout -b $BrewTapBranch 2>&1 | Out-Null
|
|
352
|
-
}
|
|
353
|
-
# Try to pull if remote branch exists
|
|
354
|
-
git -C $BrewTapDir pull origin $BrewTapBranch 2>&1 | Out-Null
|
|
355
|
-
} else {
|
|
356
|
-
Log "Empty tap repo detected - creating initial branch $BrewTapBranch"
|
|
357
|
-
git -C $BrewTapDir checkout --orphan $BrewTapBranch 2>&1 | Out-Null
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
# ── Step C: Copy formulas, commit, push ──
|
|
362
|
-
if ($tapReady) {
|
|
363
|
-
$tapFormulaDir = Join-Path $BrewTapDir "Formula"
|
|
364
|
-
if (-not (Test-Path $tapFormulaDir)) {
|
|
365
|
-
New-Item -ItemType Directory -Path $tapFormulaDir -Force | Out-Null
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
Copy-Item (Join-Path $repoRoot "packaging\homebrew\its-magic.rb") (Join-Path $tapFormulaDir "its-magic.rb") -Force
|
|
369
|
-
Copy-Item (Join-Path $repoRoot "packaging\homebrew\its-magic-beta.rb") (Join-Path $tapFormulaDir "its-magic-beta.rb") -Force
|
|
370
|
-
|
|
371
|
-
git -C $BrewTapDir add Formula/its-magic.rb Formula/its-magic-beta.rb
|
|
372
|
-
|
|
373
|
-
$tapChanges = git -C $BrewTapDir status --porcelain
|
|
374
|
-
if ($tapChanges) {
|
|
375
|
-
git -C $BrewTapDir commit -m "chore: update its-magic formulas for $newVersion" 2>&1 | Out-Null
|
|
376
|
-
if ($LASTEXITCODE -ne 0) {
|
|
377
|
-
$brewTapFailed = $true
|
|
378
|
-
Warn "Failed to commit Homebrew tap changes"
|
|
379
|
-
} else {
|
|
380
|
-
git -C $BrewTapDir push -u origin $BrewTapBranch 2>&1 | Out-Null
|
|
381
|
-
if ($LASTEXITCODE -ne 0) {
|
|
382
|
-
$brewTapFailed = $true
|
|
383
|
-
Warn "Failed to push Homebrew tap changes"
|
|
384
|
-
} else {
|
|
385
|
-
$brewTapPushed = $true
|
|
386
|
-
Log "Homebrew tap updated: https://github.com/$BrewTapRepo"
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
} else {
|
|
390
|
-
Log "No Homebrew formula changes to push"
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
$ErrorActionPreference = $prevEAP
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
} else {
|
|
399
|
-
Warn "Skipping Homebrew tap push"
|
|
400
|
-
}
|
|
401
|
-
} else {
|
|
402
|
-
Warn "Skipping Homebrew"
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
# ── Done ─────────────────────────────────────────────────────────────
|
|
406
|
-
Pop-Location
|
|
407
|
-
$npmStatus = "OK"
|
|
408
|
-
if ($SkipNpm) { $npmStatus = "SKIPPED" }
|
|
409
|
-
$chocoStatus = "OK"
|
|
410
|
-
if ($SkipChoco) { $chocoStatus = "SKIPPED" }
|
|
411
|
-
elseif ($DryRun) { $chocoStatus = "DRY-RUN" }
|
|
412
|
-
elseif ($chocoFailed) { $chocoStatus = "FAILED - check log" }
|
|
413
|
-
$brewStatus = "OK - formula updated"
|
|
414
|
-
if ($SkipBrew) { $brewStatus = "SKIPPED" }
|
|
415
|
-
elseif ($brewTapFailed) { $brewStatus = "FORMULA OK - TAP PUSH FAILED" }
|
|
416
|
-
elseif ($brewTapPushed) { $brewStatus = "OK - formula updated + tap pushed" }
|
|
417
|
-
|
|
418
|
-
Log "=========================================="
|
|
419
|
-
Log "Release $newVersion complete!"
|
|
420
|
-
Log " npm: $npmStatus"
|
|
421
|
-
Log " Chocolatey: $chocoStatus"
|
|
422
|
-
Log " Homebrew: $brewStatus"
|
|
423
|
-
Log "=========================================="
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# QA Findings — Webview UI → Backend Integration & Invalid Input Handling
|
|
2
|
-
|
|
3
|
-
**Sprint:** S0001
|
|
4
|
-
**Date:** 2026-02-06
|
|
5
|
-
**Scope:** Verify the webview UI calls the backend correctly and handles invalid input.
|
|
6
|
-
**Method:** Static code review of `examples/webview-app/` (frontend + backend + shared schema).
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## Test Plan
|
|
11
|
-
|
|
12
|
-
### Area 1 — UI → Backend API Calls
|
|
13
|
-
|
|
14
|
-
| # | Test Case | Frontend Entry Point | API Endpoint | Result |
|
|
15
|
-
|---|-----------|---------------------|-------------|--------|
|
|
16
|
-
| 1 | Initial page load fetches items | `loadItems()` on bootstrap (line 338) | `GET /api/v1/items` | PASS |
|
|
17
|
-
| 2 | Filter change reloads items | `change` listeners → `loadItems()` (lines 301-304) | `GET /api/v1/items?status=…&category=…` | PASS |
|
|
18
|
-
| 3 | Search input debounced reload | `input` → 300ms debounce → `loadItems()` (lines 305-308) | `GET /api/v1/items?search=…` | PASS |
|
|
19
|
-
| 4 | Pagination prev/next | Button clicks update `currentMeta.page` → `loadItems()` (lines 315-320) | `GET /api/v1/items?page=N` | PASS |
|
|
20
|
-
| 5 | Click item opens detail | `openDetail()` → `fetchItem(id)` (lines 215-230) | `GET /api/v1/items/:id` | PASS |
|
|
21
|
-
| 6 | Delete item calls API | `handleDelete()` → `deleteItem(id)` (lines 263-275) | `DELETE /api/v1/items/:id` | PASS |
|
|
22
|
-
| 7 | Clear/reset filters resets & reloads | `clearFilters()` → `loadItems()` (lines 252-259) | `GET /api/v1/items` (defaults) | PASS |
|
|
23
|
-
| 8 | Keyboard nav (arrows) opens detail | `navigateItem()` → `openDetail()` (lines 329-334) | `GET /api/v1/items/:id` | PASS |
|
|
24
|
-
| 9 | Request headers set Content-Type JSON | `apiFetch()` sets `Content-Type: application/json` (line 52) | All endpoints | PASS |
|
|
25
|
-
| 10 | 204 response handled (delete) | `apiFetch()` returns null on 204 (line 56) | `DELETE /api/v1/items/:id` | PASS |
|
|
26
|
-
|
|
27
|
-
### Area 2 — Invalid Input Handling
|
|
28
|
-
|
|
29
|
-
| # | Test Case | Frontend Behaviour | Backend Behaviour | Result |
|
|
30
|
-
|---|-----------|--------------------|--------------------|--------|
|
|
31
|
-
| 11 | API returns 400 validation error | `apiFetch()` throws structured Error with code/details (lines 60-64) | 400 + `VALIDATION_ERROR` envelope | PASS |
|
|
32
|
-
| 12 | List load failure shows error in UI | `loadItems()` catch renders `.error-message` div (lines 208-211) | N/A | PASS |
|
|
33
|
-
| 13 | Detail load failure feedback | `openDetail()` catch logs to console only (lines 227-229) | 404 `NOT_FOUND` | **FAIL** |
|
|
34
|
-
| 14 | Delete failure shows alert | `handleDelete()` catch calls `alert()` (lines 272-274) | 404/400 | PASS |
|
|
35
|
-
| 15 | Search maxlength enforced client-side | `<input maxlength="200">` (index.html line 50) | `validateQuery` checks ≤200 (validators.js line 149) | PASS |
|
|
36
|
-
| 16 | XSS prevention | `escapeHtml()` used for title/category rendering (lines 143, 147) | N/A | PASS |
|
|
37
|
-
| 17 | Invalid status in query params | Backend rejects with 400 | `validateQuery` checks enum (validators.js lines 137-146) | PASS |
|
|
38
|
-
| 18 | Invalid sort/order params | Backend rejects with 400 | `validateQuery` checks enum (validators.js lines 167-171) | PASS |
|
|
39
|
-
| 19 | Invalid page/pageSize params | Backend rejects with 400 | `validateQuery` checks integer + range (validators.js lines 159-164) | PASS |
|
|
40
|
-
| 20 | Non-JSON backend response | `res.json()` throws SyntaxError — unhandled gracefully | N/A | **FAIL** |
|
|
41
|
-
| 21 | Category filter not validated by backend | Unrecognized category returns empty list (no error) | No category enum validation in `validateQuery` | **WARN** |
|
|
42
|
-
| 22 | POST/PATCH body validation (title required) | N/A (no create/edit UI) | `validateBody` enforces required + length (validators.js lines 82-124) | PASS |
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Findings
|
|
47
|
-
|
|
48
|
-
### F-001 — Silent failure on item detail fetch (Medium)
|
|
49
|
-
|
|
50
|
-
**File:** `examples/webview-app/frontend/app.js` lines 224-229
|
|
51
|
-
**Severity:** Medium
|
|
52
|
-
**Category:** UX / Error handling
|
|
53
|
-
|
|
54
|
-
`openDetail()` catches fetch errors but only logs to `console.error`. The user receives no visible feedback when a detail fetch fails (e.g., item deleted by another user, network error). Compare with `loadItems()` which renders an inline error message, and `handleDelete()` which shows an `alert()`.
|
|
55
|
-
|
|
56
|
-
**Expected:** Display a user-visible error in the detail panel or a toast notification.
|
|
57
|
-
**Recommendation:** Render an error state inside `$detailPanel` or show an inline banner, consistent with the error handling in `loadItems()`.
|
|
58
|
-
|
|
59
|
-
---
|
|
60
|
-
|
|
61
|
-
### F-002 — Non-JSON error responses crash `apiFetch` (Medium)
|
|
62
|
-
|
|
63
|
-
**File:** `examples/webview-app/frontend/app.js` lines 49-68
|
|
64
|
-
**Severity:** Medium
|
|
65
|
-
**Category:** Robustness
|
|
66
|
-
|
|
67
|
-
`apiFetch()` unconditionally calls `res.json()` for all non-204 responses (line 58). If the backend returns a non-JSON response (e.g., an HTML proxy error page, a 502 from a reverse proxy, or a plain-text 500), `res.json()` will throw a `SyntaxError` instead of the structured error the callers expect.
|
|
68
|
-
|
|
69
|
-
**Expected:** Graceful handling of non-JSON responses with a fallback error message.
|
|
70
|
-
**Recommendation:** Wrap `res.json()` in a try/catch or check the `Content-Type` header before parsing.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
### F-003 — No loading/spinner state during API calls (Low)
|
|
75
|
-
|
|
76
|
-
**File:** `examples/webview-app/frontend/app.js`
|
|
77
|
-
**Severity:** Low
|
|
78
|
-
**Category:** UX
|
|
79
|
-
|
|
80
|
-
No loading indicator is shown during any API call (`loadItems`, `openDetail`, `handleDelete`). On slow connections or when the backend is unresponsive, the UI appears frozen with no feedback.
|
|
81
|
-
|
|
82
|
-
**Expected:** A spinner or skeleton state while data is loading.
|
|
83
|
-
|
|
84
|
-
---
|
|
85
|
-
|
|
86
|
-
### F-004 — Category filter values not validated on backend (Low)
|
|
87
|
-
|
|
88
|
-
**File:** `examples/webview-app/backend/validators.js` lines 132-185
|
|
89
|
-
**Severity:** Low
|
|
90
|
-
**Category:** Validation gap
|
|
91
|
-
|
|
92
|
-
`validateQuery` validates `status` against the schema enum but does not validate the `category` query parameter against any allow-list. An arbitrary category value like `?category=XSS_ATTEMPT` would simply produce an empty result set (no crash), so this is low-severity. However, it is inconsistent with the `status` validation approach.
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
### F-005 — Soft-delete confirmation implies restorability but no restore UI exists (Low)
|
|
97
|
-
|
|
98
|
-
**File:** `examples/webview-app/frontend/app.js` line 265
|
|
99
|
-
**Severity:** Low
|
|
100
|
-
**Category:** UX
|
|
101
|
-
|
|
102
|
-
The delete confirmation says _"This action is reversible (soft delete)"_, which is technically true at the data layer. However, no UI exists to undelete items, making this effectively irreversible from the user's perspective.
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## Verdict
|
|
107
|
-
|
|
108
|
-
| Severity | Count |
|
|
109
|
-
|----------|-------|
|
|
110
|
-
| Medium | 2 (F-001, F-002) |
|
|
111
|
-
| Low | 3 (F-003, F-004, F-005) |
|
|
112
|
-
|
|
113
|
-
**Overall:** The UI correctly calls all documented backend endpoints. Filter, search, pagination, detail, and delete flows all route to the correct API paths with proper parameters. Backend validation is solid with structured error envelopes. Two medium-severity gaps exist in frontend error handling that should be addressed before release: silent detail fetch failures (F-001) and non-JSON response handling (F-002). No critical/blocking defects found.
|
package/sprints/S0001/sprint.md
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
# Sprint S0001
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Deliver a working vertical slice: filtered item list with detail panel, powered by a shared-schema REST API. Users can filter items by status/category/date, view details in a slide-over panel, and create/update items — all validated end-to-end with shared Zod schemas.
|
|
6
|
-
|
|
7
|
-
## Scope
|
|
8
|
-
|
|
9
|
-
- **US-0002** — Filter Bar with Instant Results
|
|
10
|
-
- **US-0003** — Slide-Over Detail Panel
|
|
11
|
-
- **US-0004** — Empty-State & Filter Awareness
|
|
12
|
-
|
|
13
|
-
## User Stories → Task Mapping
|
|
14
|
-
|
|
15
|
-
| Story | Tasks |
|
|
16
|
-
|---------|--------------------------------|
|
|
17
|
-
| US-0002 | T-015, T-016, T-017, T-018, T-019 |
|
|
18
|
-
| US-0003 | T-010, T-016, T-020 |
|
|
19
|
-
| US-0004 | T-021 |
|
|
20
|
-
| Infra | T-001, T-002, T-003, T-007, T-008, T-022 |
|
|
21
|
-
|
|
22
|
-
## Build Order (dependency chain)
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
T-001 Scaffold workspace
|
|
26
|
-
└─ T-002 Shared Zod schemas + types
|
|
27
|
-
├─ T-003 tsconfig path aliases
|
|
28
|
-
├─ T-004 Backend validation middleware
|
|
29
|
-
└─ T-005 Frontend form validation setup
|
|
30
|
-
│
|
|
31
|
-
T-007 Express server + health check ─┐
|
|
32
|
-
T-008 Drizzle schema + SQLite setup ──┤
|
|
33
|
-
T-014 Error envelope ────────────────┘
|
|
34
|
-
│
|
|
35
|
-
T-009 GET /items (filtered + paginated)
|
|
36
|
-
T-010 GET /items/:id
|
|
37
|
-
T-011 POST /items
|
|
38
|
-
T-012 PATCH /items/:id
|
|
39
|
-
T-013 DELETE /items/:id (soft)
|
|
40
|
-
│
|
|
41
|
-
T-015 React + Vite + Tailwind shell ─┐
|
|
42
|
-
T-022 Vite dev proxy ────────────────┘
|
|
43
|
-
│
|
|
44
|
-
T-016 Typed API client + TanStack Query hooks
|
|
45
|
-
T-006 Response validation on API client
|
|
46
|
-
│
|
|
47
|
-
T-017 FilterBar component
|
|
48
|
-
T-018 useFilters hook (URL sync)
|
|
49
|
-
T-019 ItemList component
|
|
50
|
-
T-020 DetailPanel component
|
|
51
|
-
T-021 EmptyState component
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## Risks
|
|
55
|
-
|
|
56
|
-
| Risk | Likelihood | Impact | Mitigation |
|
|
57
|
-
|------|-----------|--------|------------|
|
|
58
|
-
| Shared schema drift if `/shared` is not built first | Medium | High | npm workspaces build order: shared → backend → frontend |
|
|
59
|
-
| CORS misconfiguration between SPA and API | Medium | Low | Vite proxy for dev; explicit CORS middleware for prod |
|
|
60
|
-
| SQLite → PostgreSQL dialect differences | Low | Medium | Drizzle ORM abstracts dialect; test against both in CI |
|
|
61
|
-
| Filter query params exceed URL length | Low | Low | Start with 4 dimensions; add POST filter fallback if needed |
|
|
62
|
-
|
|
63
|
-
## Definition of Done
|
|
64
|
-
|
|
65
|
-
- All 22 tasks complete and checked off in `tasks.md`
|
|
66
|
-
- Shared schemas consumed by both frontend and backend without duplication
|
|
67
|
-
- API returns structured error envelope for validation failures
|
|
68
|
-
- Filter bar updates list instantly; active filters reflected in URL
|
|
69
|
-
- Detail panel opens/closes with keyboard navigation (arrows, Escape)
|
|
70
|
-
- Empty state renders with "Reset filters" action when no results match
|
package/sprints/S0001/summary.md
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# Sprint Summary — S0001
|
|
2
|
-
|
|
3
|
-
## Goal
|
|
4
|
-
|
|
5
|
-
Deliver a working vertical slice: filtered item list with detail panel, powered by a shared-schema REST API.
|
|
6
|
-
|
|
7
|
-
## Completed
|
|
8
|
-
|
|
9
|
-
- GSD Workflow Kit: all slash commands, agent roles, rules, templates, and CI/CD workflows
|
|
10
|
-
- Installers (PowerShell, Bash, Python) for kit distribution
|
|
11
|
-
- Benchmark harness (scenario, live, headless, prompted modes)
|
|
12
|
-
- Reference app (`examples/webview-app/`):
|
|
13
|
-
- Express backend with full CRUD API (`/api/v1/items`)
|
|
14
|
-
- Shared JSON Schema for cross-layer validation
|
|
15
|
-
- Structured error envelopes (VALIDATION_ERROR, NOT_FOUND, INTERNAL_ERROR)
|
|
16
|
-
- Frontend SPA with filter bar, sortable list, pagination
|
|
17
|
-
- Slide-over detail panel with keyboard navigation
|
|
18
|
-
- Empty state with filter awareness and reset action
|
|
19
|
-
- XSS prevention, soft-delete, seed data (20 items)
|
|
20
|
-
- Architecture decisions documented (DEC-0001, DEC-0002)
|
|
21
|
-
- QA review completed — 22 test cases, 20 PASS, 2 FAIL, 1 WARN
|
|
22
|
-
|
|
23
|
-
## User Stories Delivered
|
|
24
|
-
|
|
25
|
-
| Story | Title | Status |
|
|
26
|
-
|---------|--------------------------------|-----------|
|
|
27
|
-
| US-0002 | Filter Bar with Instant Results | Delivered |
|
|
28
|
-
| US-0003 | Slide-Over Detail Panel | Delivered |
|
|
29
|
-
| US-0004 | Empty-State & Filter Awareness | Delivered |
|
|
30
|
-
|
|
31
|
-
## Open Items
|
|
32
|
-
|
|
33
|
-
- **F-001** (Medium): Silent failure on item detail fetch — needs visible user feedback
|
|
34
|
-
- **F-002** (Medium): Non-JSON error responses crash `apiFetch` — needs try/catch or Content-Type check
|
|
35
|
-
- **F-003** (Low): No loading/spinner state during API calls
|
|
36
|
-
- **F-004** (Low): Category filter values not validated on backend
|
|
37
|
-
- **F-005** (Low): Soft-delete confirmation implies restorability but no restore UI exists
|
|
38
|
-
|
|
39
|
-
## Metrics
|
|
40
|
-
|
|
41
|
-
- QA test cases: 22
|
|
42
|
-
- Pass: 20 | Fail: 2 | Warn: 1
|
|
43
|
-
- Critical/blocking defects: 0
|
|
44
|
-
- Medium findings: 2
|
|
45
|
-
- Low findings: 3
|
|
46
|
-
|