Closing the intent-to-code chasm - specification-driven development with BDD verification chain
Overall
score
96%
Does it follow best practices?
Validation for skill structure
#!/usr/bin/env pwsh
$ErrorActionPreference = "Stop"
try { $repoRoot = (git rev-parse --show-toplevel 2>$null) if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrEmpty($repoRoot)) { exit 0 } } catch { exit 0 }
$scriptsDir = $null $candidatePaths = @( (Join-Path $repoRoot ".claude/skills/iikit-core/scripts/powershell"), (Join-Path $repoRoot ".tessl/tiles/tessl-labs/intent-integrity-kit/skills/iikit-core/scripts/powershell"), (Join-Path $repoRoot ".codex/skills/iikit-core/scripts/powershell") )
foreach ($candidate in $candidatePaths) { if (Test-Path (Join-Path $candidate "testify-tdd.ps1")) { $scriptsDir = $candidate break } }
if (-not $scriptsDir) { Write-Warning "[iikit] IIKit scripts not found - skipping assertion integrity check" exit 0 }
$testifyScript = Join-Path $scriptsDir "testify-tdd.ps1"
$stagedFiles = git diff --cached --name-only 2>$null $stagedFeatureFiles = $stagedFiles | Where-Object { $_ -match 'tests/features/.*.feature$' } $stagedTestSpecs = $stagedFiles | Where-Object { $_ -match 'test-specs.md$' }
if (-not $stagedFeatureFiles -and -not $stagedTestSpecs) { exit 0 }
$constitutionFile = Join-Path $repoRoot "CONSTITUTION.md" $tddDetermination = "unknown" if (Test-Path $constitutionFile) { $tddDetermination = & $testifyScript get-tdd-determination $constitutionFile }
$blocked = $false $blockMessages = @()
$allStagedFiles = git diff --cached --name-only 2>$null
if ($stagedFeatureFiles) { # Group staged .feature files by feature directory $featureDirsMap = @{} foreach ($stagedPath in $stagedFeatureFiles) { if ([string]::IsNullOrEmpty($stagedPath)) { continue } # Derive feature dir: specs/NNN/tests/features/x.feature -> specs/NNN $featuresDir = Split-Path $stagedPath -Parent # tests/features $testsDir = Split-Path $featuresDir -Parent # tests $featDir = Split-Path $testsDir -Parent # specs/NNN-feature $featureDirsMap[$featDir] = $true }
foreach ($featDir in $featureDirsMap.Keys) {
$featuresDirAbs = Join-Path $repoRoot "$featDir/tests/features"
$contextFile = Join-Path $repoRoot "$featDir/context.json"
$contextRelPath = "$featDir/context.json"
# Extract all staged .feature files for this feature to temp dir
$tempFeaturesDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
New-Item -ItemType Directory -Path $tempFeaturesDir -Force | Out-Null
$stagedForFeat = $stagedFeatureFiles | Where-Object { $_ -match "^$([regex]::Escape($featDir))/" }
foreach ($sf in $stagedForFeat) {
if ([string]::IsNullOrEmpty($sf)) { continue }
$basename = Split-Path $sf -Leaf
try {
$content = git show ":$sf" 2>$null
if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrEmpty($content)) {
$content | Out-File -FilePath (Join-Path $tempFeaturesDir $basename) -Encoding utf8
}
} catch { continue }
}
# Compute combined hash of staged .feature files
$currentHash = & $testifyScript compute-hash $tempFeaturesDir
Remove-Item -Recurse -Force $tempFeaturesDir -ErrorAction SilentlyContinue
if ($currentHash -eq "NO_ASSERTIONS") {
continue
}
# Check if context.json is also being staged
$contextStaged = $allStagedFiles | Where-Object { $_ -eq $contextRelPath -or $_ -eq ($contextRelPath -replace '\\', '/') }
# Read context.json (staged or committed version)
$contextStatus = "missing"
$contextJson = $null
if ($contextStaged -and (Test-Path $contextFile)) {
$contextJson = Get-Content $contextFile -Raw -ErrorAction SilentlyContinue
} else {
try {
$contextJson = git show "HEAD:$($contextRelPath -replace '\\', '/')" 2>$null
if ($LASTEXITCODE -ne 0) { $contextJson = $null }
} catch { $contextJson = $null }
}
if (-not [string]::IsNullOrEmpty($contextJson)) {
try {
$context = $contextJson | ConvertFrom-Json
if ($context.testify -and $context.testify.assertion_hash) {
$storedHash = $context.testify.assertion_hash
$storedDir = if ($context.testify.PSObject.Properties.Name -contains 'features_dir') { $context.testify.features_dir } else { "" }
if (-not [string]::IsNullOrEmpty($storedHash) -and -not [string]::IsNullOrEmpty($storedDir)) {
if ($storedHash -eq $currentHash) {
$contextStatus = "valid"
} else {
$contextStatus = "invalid"
}
}
}
} catch {
# Invalid JSON or missing fields — treat as missing
}
}
# Combine results (git notes skipped for directory-based .feature files)
$hashStatus = "missing"
if ($contextStaged -and $contextStatus -eq "valid") {
$hashStatus = "valid"
} elseif ($contextStatus -eq "invalid") {
$hashStatus = "invalid"
} elseif ($contextStatus -eq "valid") {
$hashStatus = "valid"
}
# Decision logic
switch ($hashStatus) {
"valid" {
# PASS — silent
}
"invalid" {
$blocked = $true
$blockMessages += "BLOCKED: $featDir/tests/features/ - .feature assertion integrity check failed"
$blockMessages += " .feature file assertions have been modified since /iikit-05-testify generated them."
$blockMessages += " Re-run /iikit-05-testify to regenerate .feature files."
}
"missing" {
if ($tddDetermination -eq "mandatory") {
Write-Warning "[iikit] $featDir/tests/features/ - no stored assertion hash found (TDD is mandatory)"
Write-Warning "[iikit] If this is the initial testify commit, this is expected."
Write-Warning "[iikit] Otherwise, run /iikit-05-testify to generate integrity hashes."
}
}
}
}}
foreach ($stagedPath in $stagedTestSpecs) { if ([string]::IsNullOrEmpty($stagedPath)) { continue }
# Derive per-feature context.json path from the test-specs.md path
# test-specs.md is at specs/NNN-feature/tests/test-specs.md
# context.json is at specs/NNN-feature/context.json
$testsDir = Split-Path $stagedPath -Parent # specs/NNN-feature/tests
$featureDir = Split-Path $testsDir -Parent # specs/NNN-feature
$contextRelPath = Join-Path $featureDir "context.json" # specs/NNN-feature/context.json
$contextFile = Join-Path $repoRoot $contextRelPath
# Check if this feature's context.json is also being staged
$contextStaged = $allStagedFiles | Where-Object { $_ -eq $contextRelPath -or $_ -eq ($contextRelPath -replace '\\', '/') }
# Extract staged version to a temp file (check what's being committed)
$tempFile = [System.IO.Path]::GetTempFileName()
try {
$stagedContent = git show ":$stagedPath" 2>$null
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrEmpty($stagedContent)) {
Remove-Item $tempFile -ErrorAction SilentlyContinue
continue
}
$stagedContent | Out-File -FilePath $tempFile -Encoding utf8
} catch {
Remove-Item $tempFile -ErrorAction SilentlyContinue
continue
}
# Compute hash of the staged version
$currentHash = & $testifyScript compute-hash $tempFile
Remove-Item $tempFile -ErrorAction SilentlyContinue
# Skip if no assertions in the file
if ($currentHash -eq "NO_ASSERTIONS") {
continue
}
# Check against context.json
# When staged (testify commit): read working tree. When NOT staged: read
# committed version from HEAD to prevent working-tree forgery (A8b attack).
$contextStatus = "missing"
$contextJson = $null
if ($contextStaged -and (Test-Path $contextFile)) {
# Testify commit: read working tree (testify just wrote it)
$contextJson = Get-Content $contextFile -Raw -ErrorAction SilentlyContinue
} else {
# Not staged: read committed version from HEAD (tamper-resistant)
try {
$contextJson = git show "HEAD:$($contextRelPath -replace '\\', '/')" 2>$null
if ($LASTEXITCODE -ne 0) { $contextJson = $null }
} catch { $contextJson = $null }
}
if (-not [string]::IsNullOrEmpty($contextJson)) {
try {
$context = $contextJson | ConvertFrom-Json
if ($context.testify -and $context.testify.assertion_hash) {
$storedFile = ($context.testify.test_specs_file -replace '\\', '/')
$storedHash = $context.testify.assertion_hash
$normalizedStagedPath = ($stagedPath -replace '\\', '/')
# Match by path: stored file must end with the staged path
if ($storedFile -eq $normalizedStagedPath -or $storedFile.EndsWith("/$normalizedStagedPath")) {
if ($storedHash -eq $currentHash) {
$contextStatus = "valid"
} else {
$contextStatus = "invalid"
}
}
}
} catch {
# Invalid JSON or missing fields — treat as missing
}
}
# Check git notes (tamper-resistant)
# Search backward through recent commits to find the most recent testify note
# that matches THIS specific test-specs.md file (by path).
# Notes may contain multiple entries (separated by ---) for multi-feature commits.
$noteStatus = "missing"
$noteHash = $null
try {
$recentCommits = git rev-list HEAD -50 2>$null
if ($recentCommits) {
foreach ($commitSha in ($recentCommits -split "`n")) {
if ([string]::IsNullOrEmpty($commitSha)) { continue }
$noteContent = git notes --ref="refs/notes/testify" show $commitSha 2>$null
if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrEmpty($noteContent)) {
# Parse multi-entry notes: split by --- and find matching file
$entries = ($noteContent -join "`n") -split '---'
foreach ($entry in $entries) {
$entryLines = $entry.Trim() -split "`n"
$entryFile = ""
$entryHash = ""
foreach ($line in $entryLines) {
if ($line -match '^test-specs-file:\s*(.+)') { $entryFile = $Matches[1].Trim() }
if ($line -match '^testify-hash:\s*(.+)') { $entryHash = $Matches[1].Trim() }
}
if ($entryFile -eq $stagedPath -or $entryFile.EndsWith("/$stagedPath")) {
$noteHash = $entryHash
break
}
}
if (-not [string]::IsNullOrEmpty($noteHash)) { break }
# Note exists but no matching file — keep searching older commits
}
}
}
} catch {
# Git notes not available — stay missing
}
if (-not [string]::IsNullOrEmpty($noteHash)) {
if ($noteHash -eq $currentHash) {
$noteStatus = "valid"
} else {
$noteStatus = "invalid"
}
}
# Combine results
# When context.json is staged alongside test-specs.md and hashes match,
# this is a testify commit (new/updated assertions with fresh hash).
# The old git note from a previous testify run is expected to not match.
# When context.json is NOT staged, git note "invalid" overrides context "valid".
$hashStatus = "missing"
if ($contextStaged -and $contextStatus -eq "valid") {
# Testify commit: context.json staged with matching hash -> trust it
$hashStatus = "valid"
} elseif ($noteStatus -eq "invalid" -or $contextStatus -eq "invalid") {
$hashStatus = "invalid"
} elseif ($noteStatus -eq "valid" -or $contextStatus -eq "valid") {
$hashStatus = "valid"
}
# Decision logic
switch ($hashStatus) {
"valid" {
# PASS — silent
}
"invalid" {
$blocked = $true
$blockMessages += "BLOCKED: $stagedPath - assertion integrity check failed"
$blockMessages += " Assertions have been modified since /iikit-05-testify generated them."
$blockMessages += " Re-run /iikit-05-testify to regenerate test specifications."
}
"missing" {
if ($tddDetermination -eq "mandatory") {
Write-Warning "[iikit] $stagedPath - no stored assertion hash found (TDD is mandatory)"
Write-Warning "[iikit] If this is the initial testify commit, this is expected."
Write-Warning "[iikit] Otherwise, run /iikit-05-testify to generate integrity hashes."
}
# Allow in both cases
}
}}
if ($blocked) { Write-Host "" Write-Host "+---------------------------------------------------------+" -ForegroundColor Red Write-Host "| IIKIT PRE-COMMIT: ASSERTION INTEGRITY CHECK FAILED |" -ForegroundColor Red Write-Host "+---------------------------------------------------------+" -ForegroundColor Red Write-Host "" foreach ($msg in $blockMessages) { Write-Host "[iikit] $msg" -ForegroundColor Red } Write-Host "" Write-Host "[iikit] To fix: Re-run /iikit-05-testify to regenerate test specs with valid hashes." -ForegroundColor Yellow Write-Host "[iikit] To bypass (NOT recommended): git commit --no-verify" -ForegroundColor Yellow Write-Host "" exit 1 }
exit 0
Install with Tessl CLI
npx tessl i tessl-labs/intent-integrity-kitrules
skills
iikit-00-constitution
scripts
iikit-01-specify
iikit-02-clarify
iikit-03-plan
iikit-04-checklist
scripts
dashboard
iikit-05-testify
iikit-06-tasks
iikit-07-analyze
iikit-08-implement
iikit-09-taskstoissues
iikit-bugfix
scripts
iikit-core
scripts
bash
dashboard
powershell