fix: attest plans from their slug directory

This commit is contained in:
OthmanAdi
2026-08-31 10:27:24 +02:00
parent d5d35e6a23
commit 77390d2a20
34 changed files with 1048 additions and 48 deletions
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
+3 -1
View File
@@ -7,11 +7,13 @@ allowed-tools: "Bash"
Run the plan attestation helper for the active plan.
Steps:
1. Resolve the active plan: prefer `${PLAN_ID}` env var, then `.planning/.active_plan`, then newest `.planning/<dir>/`, then legacy `./task_plan.md`.
1. Resolve the active plan: prefer `${PLAN_ID}` env var, then `.planning/.active_plan`, then newest `.planning/<dir>/`. If the current directory is itself a valid `.planning/<slug>/` directory, use its `task_plan.md`. Otherwise use legacy `./task_plan.md`.
2. Compute the SHA-256 of the resolved `task_plan.md`.
3. Write the hex digest to `.planning/<active-plan>/.attestation` (parallel-plan mode) or `./.plan-attestation` (legacy mode).
4. Confirm to the user with the short hash (first 12 hex chars) and the storage path.
An explicit `${PLAN_ID}` or `${PWF_PLAN_ROOT}` that does not resolve exits with an error. It never falls back to another plan.
Implementation:
- On Linux/macOS/Git Bash: `sh ${CLAUDE_PLUGIN_ROOT}/scripts/attest-plan.sh`
- On Windows PowerShell plugin installs: `& "$env:CLAUDE_PLUGIN_ROOT\scripts\attest-plan.ps1"`
+9
View File
@@ -14,6 +14,15 @@ When you run `sh scripts/attest-plan.sh`, the script:
4. Renames the temporary file into place.
5. Uses `flock -w 5` around the rename when `flock` is available.
The shell and PowerShell helpers can run from the project root or directly from
a valid `.planning/<slug>/` directory. A direct slug-directory invocation
targets that directory's `task_plan.md` and stores the hash in its `.attestation`
file. It does not create the legacy `.plan-attestation` file beside a slug plan.
`PLAN_ID` and `PWF_PLAN_ROOT` are explicit selectors. If an explicit selector
does not resolve, the helper exits with an error instead of attesting a different
plan through the current-directory fallback.
The atomic rename is the correctness guarantee. It prevents readers from seeing
a partially written attestation file. The `flock` call is only a cooperative
gate for concurrent writers on systems that provide it.
+26 -2
View File
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
+37 -1
View File
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
@@ -382,6 +382,21 @@ function Resolve-ContainedPlanFile {
} catch { return $null }
}
function Test-SlugPlanDirectory {
param([string] $Directory)
try {
$finalDirectory = [PwfAttestationNative]::FinalDirectoryPath($Directory)
} catch {
return $false
}
$planningDirectory = Split-Path -Parent $finalDirectory
$planId = Split-Path -Leaf $finalDirectory
return (
(Split-Path -Leaf $planningDirectory) -eq ".planning" -and
$planId -match '^[A-Za-z0-9_][A-Za-z0-9._-]*$'
)
}
function Resolve-PlanFile {
$resolver = Join-Path $PSScriptRoot "resolve-plan-dir.ps1"
if (-not (Test-Path -LiteralPath $resolver -PathType Leaf)) { return $null }
@@ -397,8 +412,14 @@ function Resolve-PlanFile {
$activePointer = Join-Path (Join-Path (Get-Location) ".planning") ".active_plan"
if (Test-Path -LiteralPath $activePointer) { return $null }
$legacy = Join-Path (Get-Location) "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory (Get-Location).Path)
$currentDirectory = (Get-Location).Path
if (Test-SlugPlanDirectory $currentDirectory) {
$slugPlan = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $slugPlan -ExpectedDirectory $currentDirectory)
}
$legacy = Join-Path $currentDirectory "task_plan.md"
return (Resolve-ContainedPlanFile -Candidate $legacy -ExpectedDirectory $currentDirectory)
}
function Get-AttestationPath {
@@ -406,6 +427,9 @@ function Get-AttestationPath {
$planDir = Split-Path -Parent $PlanFile
$cwd = (Get-Location).Path
if ($planDir -eq $cwd) {
if (Test-SlugPlanDirectory $cwd) {
return (Join-Path $cwd ".attestation")
}
return (Join-Path $cwd ".plan-attestation")
}
return (Join-Path $planDir ".attestation")
@@ -9,7 +9,8 @@
# 1. $PLAN_ID env var → ./.planning/$PLAN_ID/
# 2. ./.planning/.active_plan
# 3. Newest ./.planning/<dir>/ by mtime
# 4. Legacy ./task_plan.md at project root
# 4. Current directory when it is .planning/<valid-slug>/
# 5. Legacy ./task_plan.md at project root
#
# Usage:
# sh scripts/attest-plan.sh # attest the active plan
@@ -21,6 +22,25 @@ set -u
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
RESOLVER="${SCRIPT_DIR}/resolve-plan-dir.sh"
slug_is_valid() {
case "$1" in
'') return 1 ;;
*[!A-Za-z0-9._-]*) return 1 ;;
[A-Za-z0-9_]*) return 0 ;;
esac
return 1
}
resolve_from_slug_cwd() {
slug_cwd="$(pwd -P 2>/dev/null)" || return 1
planning_dir="${slug_cwd%/*}"
[ "${planning_dir##*/}" = ".planning" ] || return 1
plan_id="${slug_cwd##*/}"
slug_is_valid "${plan_id}" || return 1
[ -f "${slug_cwd}/task_plan.md" ] || return 1
printf "%s\n" "${slug_cwd}/task_plan.md"
}
resolve_plan_file() {
plan_dir=""
if [ -f "${RESOLVER}" ]; then
@@ -30,6 +50,22 @@ resolve_plan_file() {
printf "%s\n" "${plan_dir}/task_plan.md"
return 0
fi
# Explicit selectors are bindings, not hints. If the shared resolver
# rejected one, do not attest a different plan through a cwd fallback.
if [ -n "${PWF_PLAN_ROOT:-}" ] || [ -n "${PLAN_ID:-}" ]; then
return 1
fi
# An absolute script path does not change the invoking shell's cwd. When
# that cwd is a slug plan directory, keep slug-mode storage semantics
# instead of misclassifying its task_plan.md as a legacy root plan.
slug_plan_file="$(resolve_from_slug_cwd)" || slug_plan_file=""
if [ -n "${slug_plan_file}" ]; then
printf "%s\n" "${slug_plan_file}"
return 0
fi
if [ -f "./task_plan.md" ]; then
printf "%s\n" "./task_plan.md"
return 0
+33
View File
@@ -182,6 +182,39 @@ class PowerShellAttestationContainmentTests(unittest.TestCase):
self.assertEqual(0, cleared.returncode, cleared.stderr)
self.assertFalse((plan_dir / ".attestation").exists())
def test_attest_from_inside_plan_dir_uses_slug_attestation(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
plan_dir = root / ".planning" / "safe-plan"
plan_dir.mkdir(parents=True)
plan = plan_dir / "task_plan.md"
plan.write_text("# First\n", encoding="utf-8")
(root / ".planning" / ".active_plan").write_text(
"safe-plan\n", encoding="utf-8"
)
initial = self._run(root)
self.assertEqual(0, initial.returncode, initial.stderr)
attestation = plan_dir / ".attestation"
initial_hash = attestation.read_text(encoding="ascii").strip()
plan.write_text("# Second\n", encoding="utf-8")
nested = self._run(plan_dir)
self.assertEqual(0, nested.returncode, nested.stderr)
self.assertNotEqual(
initial_hash, attestation.read_text(encoding="ascii").strip()
)
self.assertFalse((plan_dir / ".plan-attestation").exists())
shown = self._run(plan_dir, None, "-Show")
self.assertEqual(0, shown.returncode, shown.stderr)
self.assertIn("SHA-256:", shown.stdout)
cleared = self._run(plan_dir, None, "-Clear")
self.assertEqual(0, cleared.returncode, cleared.stderr)
self.assertFalse(attestation.exists())
def test_traversal_plan_id_cannot_attest_legacy_fallback(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
+58 -2
View File
@@ -49,10 +49,21 @@ class PlanAttestationTests(unittest.TestCase):
def tearDown(self) -> None:
shutil.rmtree(self.tmp, ignore_errors=True)
def _run(self, *args: str) -> subprocess.CompletedProcess:
def _run(
self,
*args: str,
cwd: Path | None = None,
env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess:
run_env = os.environ.copy()
run_env.pop("PLAN_ID", None)
run_env.pop("PWF_PLAN_ROOT", None)
if env:
run_env.update(env)
return subprocess.run(
["sh", str(self.scripts_dir / "attest-plan.sh"), *args],
cwd=str(self.tmp),
cwd=str(cwd or self.tmp),
env=run_env,
text=True,
encoding="utf-8",
capture_output=True,
@@ -122,6 +133,51 @@ class PlanAttestationTests(unittest.TestCase):
"must not write the legacy file when an active plan dir exists",
)
def test_attest_from_inside_plan_dir_updates_slug_attestation(self) -> None:
plan_dir = self.tmp / ".planning" / "2026-05-05-feature-x"
plan_dir.mkdir(parents=True)
plan = plan_dir / "task_plan.md"
plan.write_text("phase A\n", encoding="utf-8")
(self.tmp / ".planning" / ".active_plan").write_text(
"2026-05-05-feature-x", encoding="utf-8"
)
initial = self._run()
self.assertEqual(0, initial.returncode, initial.stderr)
attest = plan_dir / ".attestation"
initial_hash = attest.read_text(encoding="utf-8").strip()
plan.write_text("phase A\nphase B\n", encoding="utf-8")
nested = self._run(cwd=plan_dir)
self.assertEqual(0, nested.returncode, nested.stderr)
self.assertNotEqual(initial_hash, sha256_of(plan))
self.assertEqual(sha256_of(plan), attest.read_text(encoding="utf-8").strip())
self.assertFalse(
(plan_dir / ".plan-attestation").exists(),
"nested slug invocation must not create a legacy attestation",
)
def test_failed_explicit_selector_does_not_fallback_inside_slug(self) -> None:
plan_dir = self.tmp / ".planning" / "2026-05-05-feature-x"
plan_dir.mkdir(parents=True)
(plan_dir / "task_plan.md").write_text("phase A\n", encoding="utf-8")
selectors = {
"PLAN_ID": "missing-plan",
"PWF_PLAN_ROOT": str(self.tmp / "missing-project"),
}
for name, value in selectors.items():
with self.subTest(selector=name):
(plan_dir / ".attestation").unlink(missing_ok=True)
(plan_dir / ".plan-attestation").unlink(missing_ok=True)
result = self._run(cwd=plan_dir, env={name: value})
self.assertNotEqual(0, result.returncode)
self.assertFalse((plan_dir / ".attestation").exists())
self.assertFalse((plan_dir / ".plan-attestation").exists())
def test_no_plan_exits_nonzero(self) -> None:
result = self._run()
self.assertNotEqual(0, result.returncode)