fix: attest plans from their slug directory
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user