specky-sdd 3.10.2 → 3.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.apm/agents/specky-design-architect.agent.md +8 -28
- package/.apm/agents/specky-implementer.agent.md +15 -27
- package/.apm/agents/specky-onboarding.agent.md +8 -49
- package/.apm/agents/specky-orchestrator.agent.md +12 -37
- package/.apm/agents/specky-quality-reviewer.agent.md +9 -30
- package/.apm/agents/specky-release-engineer.agent.md +8 -30
- package/.apm/agents/specky-requirements-engineer.agent.md +1 -1
- package/.apm/agents/specky-research-analyst.agent.md +1 -1
- package/.apm/agents/specky-sdd-clarify.agent.md +1 -1
- package/.apm/agents/specky-sdd-init.agent.md +8 -19
- package/.apm/agents/specky-spec-engineer.agent.md +8 -30
- package/.apm/agents/specky-task-planner.agent.md +7 -26
- package/.apm/agents/specky-test-verifier.agent.md +7 -17
- package/.apm/hooks/scripts/specky-artifact-validator.sh +14 -10
- package/.apm/hooks/scripts/specky-auto-checkpoint.sh +9 -4
- package/.apm/hooks/scripts/specky-branch-validator.sh +29 -116
- package/.apm/hooks/scripts/specky-cognitive-debt-alert.sh +6 -3
- package/.apm/hooks/scripts/specky-contract-context.bash +24 -0
- package/.apm/hooks/scripts/specky-contract-context.mjs +219 -0
- package/.apm/hooks/scripts/specky-drift-monitor.sh +6 -3
- package/.apm/hooks/scripts/specky-ears-validator.sh +5 -2
- package/.apm/hooks/scripts/specky-lgtm-gate.sh +12 -10
- package/.apm/hooks/scripts/specky-metrics-dashboard.sh +5 -2
- package/.apm/hooks/scripts/specky-phase-gate.sh +18 -9
- package/.apm/hooks/scripts/specky-pipeline-guard.sh +19 -26
- package/.apm/hooks/scripts/specky-release-gate.sh +38 -46
- package/.apm/hooks/scripts/specky-security-scan.sh +53 -36
- package/.apm/hooks/scripts/specky-session-banner.sh +22 -74
- package/.apm/hooks/scripts/specky-spec-quality.sh +7 -4
- package/.apm/hooks/scripts/specky-spec-sync.sh +12 -11
- package/.apm/hooks/scripts/specky-task-tracer.sh +5 -2
- package/.apm/hooks/specky-run.sh +3 -0
- package/.apm/instructions/claude-instructions.instructions.md +3 -3
- package/.apm/instructions/copilot-instructions.instructions.md +3 -3
- package/.apm/instructions/cursor-instructions.instructions.md +3 -3
- package/.apm/instructions/opencode-instructions.instructions.md +2 -2
- package/.apm/prompts/specky-brownfield.prompt.md +8 -5
- package/.apm/prompts/specky-greenfield.prompt.md +7 -5
- package/.apm/prompts/specky-implement.prompt.md +1 -1
- package/.apm/prompts/specky-orchestrate.prompt.md +1 -1
- package/.apm/skills/specky-design-architect/SKILL.md +39 -16
- package/.apm/skills/specky-implementer/SKILL.md +46 -158
- package/.apm/skills/specky-onboarding/SKILL.md +50 -156
- package/.apm/skills/specky-orchestrator/SKILL.md +28 -26
- package/.apm/skills/specky-quality-reviewer/SKILL.md +38 -23
- package/.apm/skills/specky-release-engineer/SKILL.md +38 -270
- package/.apm/skills/specky-research-analyst/SKILL.md +1 -1
- package/.apm/skills/specky-sdd-init/SKILL.md +61 -23
- package/.apm/skills/specky-sdd-pipeline/SKILL.md +36 -216
- package/.apm/skills/specky-spec-engineer/SKILL.md +40 -17
- package/.apm/skills/specky-task-planner/SKILL.md +31 -12
- package/.apm/skills/specky-test-verifier/SKILL.md +23 -202
- package/CHANGELOG.md +39 -0
- package/README.md +49 -47
- package/SECURITY.md +5 -1
- package/apm-policy.yml +2 -0
- package/apm.lock.yaml +55 -53
- package/apm.yml +3 -3
- package/config.yml +32 -53
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +106 -20
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +105 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/migrate-contracts.d.ts +15 -0
- package/dist/cli/commands/migrate-contracts.d.ts.map +1 -0
- package/dist/cli/commands/migrate-contracts.js +311 -0
- package/dist/cli/commands/migrate-contracts.js.map +1 -0
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +63 -48
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/index.js +82 -27
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/apm/policy.d.ts +1 -0
- package/dist/cli/lib/apm/policy.d.ts.map +1 -1
- package/dist/cli/lib/apm/policy.js +15 -0
- package/dist/cli/lib/apm/policy.js.map +1 -1
- package/dist/cli/lib/asset-copier.d.ts +1 -0
- package/dist/cli/lib/asset-copier.d.ts.map +1 -1
- package/dist/cli/lib/asset-copier.js +4 -4
- package/dist/cli/lib/asset-copier.js.map +1 -1
- package/dist/cli/lib/cursor-plugin-writer.d.ts.map +1 -1
- package/dist/cli/lib/cursor-plugin-writer.js +34 -9
- package/dist/cli/lib/cursor-plugin-writer.js.map +1 -1
- package/dist/cli/lib/harness/compilers/claude.js +3 -3
- package/dist/cli/lib/harness/compilers/claude.js.map +1 -1
- package/dist/cli/lib/harness/compilers/common.d.ts +10 -1
- package/dist/cli/lib/harness/compilers/common.d.ts.map +1 -1
- package/dist/cli/lib/harness/compilers/common.js +71 -8
- package/dist/cli/lib/harness/compilers/common.js.map +1 -1
- package/dist/cli/lib/harness/compilers/copilot.js +3 -3
- package/dist/cli/lib/harness/compilers/copilot.js.map +1 -1
- package/dist/cli/lib/harness/compilers/cursor.js +3 -3
- package/dist/cli/lib/harness/compilers/cursor.js.map +1 -1
- package/dist/cli/lib/harness/compilers/opencode.js +3 -3
- package/dist/cli/lib/harness/compilers/opencode.js.map +1 -1
- package/dist/cli/lib/harness/tool-map.d.ts +8 -1
- package/dist/cli/lib/harness/tool-map.d.ts.map +1 -1
- package/dist/cli/lib/harness/tool-map.js +74 -0
- package/dist/cli/lib/harness/tool-map.js.map +1 -1
- package/dist/cli/lib/harness/types.d.ts +11 -1
- package/dist/cli/lib/harness/types.d.ts.map +1 -1
- package/dist/cli/lib/mcp-writer.d.ts +2 -1
- package/dist/cli/lib/mcp-writer.d.ts.map +1 -1
- package/dist/cli/lib/mcp-writer.js +13 -5
- package/dist/cli/lib/mcp-writer.js.map +1 -1
- package/dist/cli/lib/settings-merger.d.ts +15 -0
- package/dist/cli/lib/settings-merger.d.ts.map +1 -1
- package/dist/cli/lib/settings-merger.js +66 -1
- package/dist/cli/lib/settings-merger.js.map +1 -1
- package/dist/config.d.ts +110 -42
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +129 -140
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/contracts/pipeline-profiles.d.ts +61 -0
- package/dist/contracts/pipeline-profiles.d.ts.map +1 -0
- package/dist/contracts/pipeline-profiles.js +303 -0
- package/dist/contracts/pipeline-profiles.js.map +1 -0
- package/dist/contracts/tool-contracts.d.ts +449 -0
- package/dist/contracts/tool-contracts.d.ts.map +1 -0
- package/dist/contracts/tool-contracts.js +83 -0
- package/dist/contracts/tool-contracts.js.map +1 -0
- package/dist/contracts/use-case.d.ts +736 -0
- package/dist/contracts/use-case.d.ts.map +1 -0
- package/dist/contracts/use-case.js +352 -0
- package/dist/contracts/use-case.js.map +1 -0
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/schemas/common.d.ts +3 -3
- package/dist/schemas/common.d.ts.map +1 -1
- package/dist/schemas/common.js +1 -4
- package/dist/schemas/common.js.map +1 -1
- package/dist/schemas/context.d.ts +2 -2
- package/dist/schemas/environment.d.ts +6 -17
- package/dist/schemas/environment.d.ts.map +1 -1
- package/dist/schemas/environment.js +1 -28
- package/dist/schemas/environment.js.map +1 -1
- package/dist/schemas/feature-content.d.ts +116 -0
- package/dist/schemas/feature-content.d.ts.map +1 -0
- package/dist/schemas/feature-content.js +65 -0
- package/dist/schemas/feature-content.js.map +1 -0
- package/dist/schemas/infrastructure.d.ts +6 -28
- package/dist/schemas/infrastructure.d.ts.map +1 -1
- package/dist/schemas/infrastructure.js +1 -30
- package/dist/schemas/infrastructure.js.map +1 -1
- package/dist/schemas/input.d.ts +412 -8
- package/dist/schemas/input.d.ts.map +1 -1
- package/dist/schemas/input.js +6 -7
- package/dist/schemas/input.js.map +1 -1
- package/dist/schemas/integration.d.ts +22 -28
- package/dist/schemas/integration.d.ts.map +1 -1
- package/dist/schemas/integration.js +11 -47
- package/dist/schemas/integration.js.map +1 -1
- package/dist/schemas/metrics.d.ts +3 -2
- package/dist/schemas/metrics.d.ts.map +1 -1
- package/dist/schemas/metrics.js +2 -1
- package/dist/schemas/metrics.js.map +1 -1
- package/dist/schemas/pbt.d.ts +2 -7
- package/dist/schemas/pbt.d.ts.map +1 -1
- package/dist/schemas/pbt.js +1 -9
- package/dist/schemas/pbt.js.map +1 -1
- package/dist/schemas/pipeline.d.ts +282 -35
- package/dist/schemas/pipeline.d.ts.map +1 -1
- package/dist/schemas/pipeline.js +29 -15
- package/dist/schemas/pipeline.js.map +1 -1
- package/dist/schemas/quality.d.ts +15 -19
- package/dist/schemas/quality.d.ts.map +1 -1
- package/dist/schemas/quality.js +6 -3
- package/dist/schemas/quality.js.map +1 -1
- package/dist/schemas/routing.d.ts +2 -2
- package/dist/schemas/routing.d.ts.map +1 -1
- package/dist/schemas/routing.js +2 -4
- package/dist/schemas/routing.js.map +1 -1
- package/dist/schemas/testing.d.ts +4 -13
- package/dist/schemas/testing.d.ts.map +1 -1
- package/dist/schemas/testing.js +2 -10
- package/dist/schemas/testing.js.map +1 -1
- package/dist/schemas/transcript.d.ts +648 -10
- package/dist/schemas/transcript.d.ts.map +1 -1
- package/dist/schemas/transcript.js +25 -12
- package/dist/schemas/transcript.js.map +1 -1
- package/dist/schemas/utility.d.ts +28 -15
- package/dist/schemas/utility.d.ts.map +1 -1
- package/dist/schemas/utility.js +23 -14
- package/dist/schemas/utility.js.map +1 -1
- package/dist/schemas/visualization.d.ts +66 -15
- package/dist/schemas/visualization.d.ts.map +1 -1
- package/dist/schemas/visualization.js +36 -12
- package/dist/schemas/visualization.js.map +1 -1
- package/dist/services/codebase-scanner.d.ts +1 -1
- package/dist/services/codebase-scanner.d.ts.map +1 -1
- package/dist/services/codebase-scanner.js +2 -2
- package/dist/services/codebase-scanner.js.map +1 -1
- package/dist/services/compliance-engine.d.ts +3 -2
- package/dist/services/compliance-engine.d.ts.map +1 -1
- package/dist/services/compliance-engine.js +34 -21
- package/dist/services/compliance-engine.js.map +1 -1
- package/dist/services/diagram-generator.d.ts +4 -96
- package/dist/services/diagram-generator.d.ts.map +1 -1
- package/dist/services/diagram-generator.js +18 -1051
- package/dist/services/diagram-generator.js.map +1 -1
- package/dist/services/doc-generator.d.ts +19 -52
- package/dist/services/doc-generator.d.ts.map +1 -1
- package/dist/services/doc-generator.js +389 -524
- package/dist/services/doc-generator.js.map +1 -1
- package/dist/services/execution-context.d.ts +32 -0
- package/dist/services/execution-context.d.ts.map +1 -0
- package/dist/services/execution-context.js +120 -0
- package/dist/services/execution-context.js.map +1 -0
- package/dist/services/file-manager.d.ts +16 -4
- package/dist/services/file-manager.d.ts.map +1 -1
- package/dist/services/file-manager.js +81 -5
- package/dist/services/file-manager.js.map +1 -1
- package/dist/services/iac-generator.d.ts +9 -10
- package/dist/services/iac-generator.d.ts.map +1 -1
- package/dist/services/iac-generator.js +36 -99
- package/dist/services/iac-generator.js.map +1 -1
- package/dist/services/methodology.js +1 -1
- package/dist/services/methodology.js.map +1 -1
- package/dist/services/metrics-generator.d.ts +1 -1
- package/dist/services/metrics-generator.d.ts.map +1 -1
- package/dist/services/metrics-generator.js +2 -2
- package/dist/services/metrics-generator.js.map +1 -1
- package/dist/services/pbt-generator.d.ts +10 -31
- package/dist/services/pbt-generator.d.ts.map +1 -1
- package/dist/services/pbt-generator.js +43 -515
- package/dist/services/pbt-generator.js.map +1 -1
- package/dist/services/state-machine.d.ts +22 -16
- package/dist/services/state-machine.d.ts.map +1 -1
- package/dist/services/state-machine.js +176 -179
- package/dist/services/state-machine.js.map +1 -1
- package/dist/services/template-engine.d.ts +11 -5
- package/dist/services/template-engine.d.ts.map +1 -1
- package/dist/services/template-engine.js +48 -21
- package/dist/services/template-engine.js.map +1 -1
- package/dist/services/test-generator.d.ts +11 -7
- package/dist/services/test-generator.d.ts.map +1 -1
- package/dist/services/test-generator.js +45 -54
- package/dist/services/test-generator.js.map +1 -1
- package/dist/templates/design.md +6 -0
- package/dist/tools/analysis.d.ts.map +1 -1
- package/dist/tools/analysis.js +8 -9
- package/dist/tools/analysis.js.map +1 -1
- package/dist/tools/checkpoint.d.ts.map +1 -1
- package/dist/tools/checkpoint.js +43 -46
- package/dist/tools/checkpoint.js.map +1 -1
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +5 -7
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/documentation.d.ts +1 -4
- package/dist/tools/documentation.d.ts.map +1 -1
- package/dist/tools/documentation.js +112 -193
- package/dist/tools/documentation.js.map +1 -1
- package/dist/tools/environment.d.ts +1 -1
- package/dist/tools/environment.d.ts.map +1 -1
- package/dist/tools/environment.js +58 -99
- package/dist/tools/environment.js.map +1 -1
- package/dist/tools/infrastructure.d.ts.map +1 -1
- package/dist/tools/infrastructure.js +36 -38
- package/dist/tools/infrastructure.js.map +1 -1
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +18 -11
- package/dist/tools/input.js.map +1 -1
- package/dist/tools/integration.d.ts.map +1 -1
- package/dist/tools/integration.js +64 -65
- package/dist/tools/integration.js.map +1 -1
- package/dist/tools/metrics.d.ts.map +1 -1
- package/dist/tools/metrics.js +18 -30
- package/dist/tools/metrics.js.map +1 -1
- package/dist/tools/pbt.d.ts.map +1 -1
- package/dist/tools/pbt.js +10 -12
- package/dist/tools/pbt.js.map +1 -1
- package/dist/tools/pipeline.d.ts.map +1 -1
- package/dist/tools/pipeline.js +156 -274
- package/dist/tools/pipeline.js.map +1 -1
- package/dist/tools/quality.d.ts.map +1 -1
- package/dist/tools/quality.js +58 -45
- package/dist/tools/quality.js.map +1 -1
- package/dist/tools/response-builder.d.ts.map +1 -1
- package/dist/tools/response-builder.js +12 -10
- package/dist/tools/response-builder.js.map +1 -1
- package/dist/tools/testing.d.ts.map +1 -1
- package/dist/tools/testing.js +28 -29
- package/dist/tools/testing.js.map +1 -1
- package/dist/tools/tool-enforcement.d.ts +2 -0
- package/dist/tools/tool-enforcement.d.ts.map +1 -1
- package/dist/tools/tool-enforcement.js +84 -59
- package/dist/tools/tool-enforcement.js.map +1 -1
- package/dist/tools/transcript.d.ts.map +1 -1
- package/dist/tools/transcript.js +304 -374
- package/dist/tools/transcript.js.map +1 -1
- package/dist/tools/turnkey.d.ts +1 -31
- package/dist/tools/turnkey.d.ts.map +1 -1
- package/dist/tools/turnkey.js +104 -488
- package/dist/tools/turnkey.js.map +1 -1
- package/dist/tools/utility.d.ts.map +1 -1
- package/dist/tools/utility.js +115 -134
- package/dist/tools/utility.js.map +1 -1
- package/dist/tools/visualization.d.ts +1 -25
- package/dist/tools/visualization.d.ts.map +1 -1
- package/dist/tools/visualization.js +187 -562
- package/dist/tools/visualization.js.map +1 -1
- package/dist/types.d.ts +21 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/artifact-metadata.d.ts +8 -0
- package/dist/utils/artifact-metadata.d.ts.map +1 -0
- package/dist/utils/artifact-metadata.js +10 -0
- package/dist/utils/artifact-metadata.js.map +1 -0
- package/dist/utils/requirement-partition.d.ts +8 -0
- package/dist/utils/requirement-partition.d.ts.map +1 -0
- package/dist/utils/requirement-partition.js +15 -0
- package/dist/utils/requirement-partition.js.map +1 -0
- package/package.json +11 -2
- package/templates/design.md +6 -0
- package/dist/utils/design-stubs.d.ts +0 -32
- package/dist/utils/design-stubs.d.ts.map +0 -1
- package/dist/utils/design-stubs.js +0 -103
- package/dist/utils/design-stubs.js.map +0 -1
|
@@ -4,20 +4,20 @@
|
|
|
4
4
|
# Ensures required artifacts exist before a phase tool runs
|
|
5
5
|
|
|
6
6
|
set -euo pipefail
|
|
7
|
+
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
8
|
+
source "$SCRIPT_DIR/specky-contract-context.bash"
|
|
7
9
|
|
|
8
10
|
TOOL="${SDD_TOOL_NAME:-unknown}"
|
|
9
|
-
LATEST=$(ls -td .specs/*/ 2>/dev/null | head -1 || true)
|
|
10
11
|
FAILS=0
|
|
11
12
|
|
|
12
|
-
if [
|
|
13
|
-
|
|
14
|
-
echo "❌ No .specs/ directory found. Run @specky-sdd-init first."
|
|
15
|
-
exit 2
|
|
16
|
-
fi
|
|
13
|
+
if [ "$TOOL" = "sdd_init" ]; then
|
|
14
|
+
echo "✅ Artifact Validator — sdd_init has no existing feature prerequisites"
|
|
17
15
|
exit 0
|
|
18
16
|
fi
|
|
17
|
+
specky_require_feature_context || exit $?
|
|
18
|
+
LATEST="$SPECKY_FEATURE_DIR"
|
|
19
19
|
|
|
20
|
-
FEATURE
|
|
20
|
+
FEATURE="${SPECKY_FEATURE_NUMBER}-${SPECKY_FEATURE_NAME}"
|
|
21
21
|
echo "📄 Artifact Validator — $FEATURE — before $TOOL"
|
|
22
22
|
|
|
23
23
|
check_exists() {
|
|
@@ -44,7 +44,7 @@ case "$TOOL" in
|
|
|
44
44
|
sdd_init)
|
|
45
45
|
echo " ✅ No prerequisites for sdd_init"
|
|
46
46
|
;;
|
|
47
|
-
sdd_discover|sdd_research|
|
|
47
|
+
sdd_discover|sdd_research|sdd_import_transcript)
|
|
48
48
|
check_exists "CONSTITUTION.md" "CONSTITUTION.md" "Run @specky-sdd-init or /specky-greenfield"
|
|
49
49
|
;;
|
|
50
50
|
sdd_clarify|sdd_validate_ears)
|
|
@@ -53,7 +53,7 @@ case "$TOOL" in
|
|
|
53
53
|
sdd_write_spec|sdd_turnkey_spec|sdd_figma_to_spec)
|
|
54
54
|
check_exists "CONSTITUTION.md" "CONSTITUTION.md" "Run @specky-sdd-init"
|
|
55
55
|
;;
|
|
56
|
-
sdd_write_design|sdd_generate_all_diagrams)
|
|
56
|
+
sdd_write_design|sdd_generate_all_diagrams|sdd_generate_diagram|sdd_figma_diagram)
|
|
57
57
|
check_exists "SPECIFICATION.md" "SPECIFICATION.md" "Run @specky-spec-engineer or /specky-specify"
|
|
58
58
|
check_content "SPECIFICATION.md" "REQ-[A-Z]+-[0-9]+" "EARS requirements with REQ-IDs"
|
|
59
59
|
;;
|
|
@@ -70,13 +70,17 @@ case "$TOOL" in
|
|
|
70
70
|
check_exists "TASKS.md" "TASKS.md" "Run /specky-tasks"
|
|
71
71
|
;;
|
|
72
72
|
sdd_run_analysis|sdd_cross_analyze)
|
|
73
|
-
check_exists "VERIFICATION.md" "VERIFICATION.md" "Run @specky-test-verifier or /specky-verify"
|
|
74
73
|
check_exists "SPECIFICATION.md" "SPECIFICATION.md" "Run /specky-specify"
|
|
75
74
|
check_exists "DESIGN.md" "DESIGN.md" "Run /specky-design"
|
|
76
75
|
check_exists "TASKS.md" "TASKS.md" "Run /specky-tasks"
|
|
77
76
|
;;
|
|
78
77
|
sdd_compliance_check)
|
|
78
|
+
if ! specky_has_capability "compliance"; then
|
|
79
|
+
echo "🚫 Compliance capability is not enabled by $SPECKY_CONTRACT_ID"
|
|
80
|
+
FAILS=$((FAILS+1))
|
|
81
|
+
fi
|
|
79
82
|
check_exists "SPECIFICATION.md" "SPECIFICATION.md" "Run /specky-specify"
|
|
83
|
+
check_exists "DESIGN.md" "DESIGN.md" "Run /specky-design"
|
|
80
84
|
;;
|
|
81
85
|
sdd_create_pr|sdd_export_work_items)
|
|
82
86
|
check_exists "ANALYSIS.md" "ANALYSIS.md" "Run @specky-quality-reviewer"
|
|
@@ -5,16 +5,21 @@
|
|
|
5
5
|
# Paper: arXiv:2602.20478 — artifact preservation
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
9
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
10
|
+
specky_load_contract_context || exit $?
|
|
11
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
12
|
+
[ "${SPECKY_CHECKPOINTS_REQUIRED:-0}" = "1" ] || exit 0
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
MARKER="/tmp/.sdd-last-checkpoint-${SPECKY_CONTRACT_FINGERPRINT}"
|
|
15
|
+
[ -f "$MARKER" ] || touch -t 197001010000 "$MARKER"
|
|
16
|
+
RECENT=$(find "$SPECKY_FEATURE_DIR" -name "*.md" -newer "$MARKER" 2>/dev/null | head -5 || true)
|
|
12
17
|
|
|
13
18
|
if [ -n "$RECENT" ]; then
|
|
14
19
|
echo "💾 Spec artifacts modified:"
|
|
15
20
|
echo "$RECENT"
|
|
16
21
|
echo " Consider running sdd_checkpoint to save a snapshot."
|
|
17
|
-
touch
|
|
22
|
+
touch "$MARKER"
|
|
18
23
|
fi
|
|
19
24
|
|
|
20
25
|
exit 0
|
|
@@ -1,131 +1,44 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# specky-branch-validator.sh — Validate
|
|
3
|
-
#
|
|
4
|
-
# Type: Mixed (see below) | Trigger: PreToolUse | All phases
|
|
5
|
-
# • Advisory (exit 0, warn) for sdd_* pipeline tools (always).
|
|
6
|
-
# • For native edit tools (Write|Edit|MultiEdit) on a wrong branch during
|
|
7
|
-
# an active pipeline:
|
|
8
|
-
# - default (SPECKY_GUARD unset OR "off"/"advisory") → warn, exit 0
|
|
9
|
-
# - SPECKY_GUARD=strict → exit 2 (BLOCK)
|
|
10
|
-
#
|
|
11
|
-
# Rationale (rc.13): rc.10-rc.12 defaulted Write|Edit|MultiEdit to BLOCKING,
|
|
12
|
-
# which burned pilots who run `specky init` and then edit anything from
|
|
13
|
-
# develop/main. Enforcement is now explicit opt-in.
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# specky-branch-validator.sh — Validate persisted release branch policy.
|
|
3
|
+
# Type: advisory by default; strict for native edits when SPECKY_GUARD=strict.
|
|
14
4
|
|
|
15
5
|
set -euo pipefail
|
|
6
|
+
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
7
|
+
source "$SCRIPT_DIR/specky-contract-context.bash"
|
|
16
8
|
|
|
17
9
|
TOOL="${SDD_TOOL_NAME:-unknown}"
|
|
18
10
|
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr -d '\n' || true)
|
|
19
11
|
[ -z "$CURRENT_BRANCH" ] && CURRENT_BRANCH="unknown"
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
13
|
+
specky_load_contract_context || exit $?
|
|
14
|
+
if [ "${SPECKY_CONTEXT_ACTIVE:-0}" != "1" ]; then
|
|
15
|
+
echo "ℹ️ Branch Validator — no explicit feature context; no branch inferred."
|
|
16
|
+
exit 0
|
|
25
17
|
fi
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if [[ "$TOOL" == "Write" || "$TOOL" == "Edit" || "$TOOL" == "MultiEdit" ]]; then
|
|
29
|
-
LATEST=$(ls -td .specs/*/ 2>/dev/null | head -1 || true)
|
|
30
|
-
if [ -z "$LATEST" ]; then exit 0; fi
|
|
31
|
-
STATE="$LATEST/.sdd-state.json"
|
|
32
|
-
if [ ! -f "$STATE" ]; then exit 0; fi
|
|
33
|
-
|
|
34
|
-
PHASE="?"
|
|
35
|
-
if command -v jq >/dev/null 2>&1; then
|
|
36
|
-
PHASE=$(jq -r '.current_phase // "?"' "$STATE" 2>/dev/null || echo "?")
|
|
37
|
-
fi
|
|
38
|
-
|
|
39
|
-
FEATURE=$(basename "$LATEST")
|
|
40
|
-
|
|
41
|
-
emit_violation() {
|
|
42
|
-
local expected="$1"
|
|
43
|
-
if [ "$SPECKY_GUARD_MODE" = "strict" ]; then
|
|
44
|
-
echo "" >&2
|
|
45
|
-
echo "🚫 [specky-branch-validator] BLOCKED — $TOOL on '$CURRENT_BRANCH' (phase $PHASE expects $expected) [SPECKY_GUARD=strict]" >&2
|
|
46
|
-
echo " Feature: $FEATURE" >&2
|
|
47
|
-
echo " Fix: git checkout $expected (or: git checkout -b spec/$FEATURE)" >&2
|
|
48
|
-
echo " Disable: unset SPECKY_GUARD" >&2
|
|
49
|
-
exit 2
|
|
50
|
-
fi
|
|
51
|
-
echo "⚠️ [specky-branch-validator] $TOOL on '$CURRENT_BRANCH' during phase $PHASE (expected $expected). Enforce with: export SPECKY_GUARD=strict" >&2
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
case "$PHASE" in
|
|
55
|
-
init|discover|specify|clarify|design|tasks|analyze|implement)
|
|
56
|
-
if [[ "$CURRENT_BRANCH" != spec/* ]]; then
|
|
57
|
-
emit_violation "spec/$FEATURE"
|
|
58
|
-
fi
|
|
59
|
-
;;
|
|
60
|
-
verify)
|
|
61
|
-
if [[ "$CURRENT_BRANCH" != "develop" ]]; then
|
|
62
|
-
emit_violation "develop"
|
|
63
|
-
fi
|
|
64
|
-
;;
|
|
65
|
-
release)
|
|
66
|
-
if [[ "$CURRENT_BRANCH" != "stage" ]]; then
|
|
67
|
-
emit_violation "stage"
|
|
68
|
-
fi
|
|
69
|
-
;;
|
|
70
|
-
esac
|
|
18
|
+
if [ "${SPECKY_RELEASE_ENABLED:-0}" != "1" ]; then
|
|
19
|
+
echo "ℹ️ Branch Validator — release capability disabled by $SPECKY_CONTRACT_ID."
|
|
71
20
|
exit 0
|
|
72
21
|
fi
|
|
73
22
|
|
|
74
|
-
|
|
23
|
+
EXPECTED_FEATURE_BRANCH="${SPECKY_BRANCH_PREFIX}${SPECKY_FEATURE_NUMBER}-${SPECKY_FEATURE_NAME}"
|
|
24
|
+
EXPECTED="$EXPECTED_FEATURE_BRANCH"
|
|
25
|
+
if [ "$TOOL" = "sdd_create_branch" ]; then
|
|
26
|
+
EXPECTED="$SPECKY_BASE_BRANCH"
|
|
27
|
+
fi
|
|
75
28
|
|
|
76
|
-
|
|
77
|
-
echo "
|
|
78
|
-
|
|
79
|
-
|
|
29
|
+
if [ "$CURRENT_BRANCH" = "$EXPECTED" ]; then
|
|
30
|
+
echo "✅ Branch Validator — $CURRENT_BRANCH matches persisted release policy."
|
|
31
|
+
exit 0
|
|
32
|
+
fi
|
|
80
33
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
fi
|
|
87
|
-
;;
|
|
88
|
-
sdd_write_spec|sdd_turnkey_spec|sdd_figma_to_spec|sdd_write_design|sdd_write_tasks|sdd_implement|sdd_generate_tests|sdd_generate_pbt|sdd_generate_iac|sdd_generate_dockerfile)
|
|
89
|
-
# Spec work must be on spec/* branch
|
|
90
|
-
if [[ "$CURRENT_BRANCH" != spec/* ]]; then
|
|
91
|
-
warn "Spec work should be on a spec/NNN-* branch, not '$CURRENT_BRANCH'" "spec/NNN-feature-name"
|
|
92
|
-
echo " Flow: spec/NNN-* → develop → stage → main"
|
|
93
|
-
fi
|
|
94
|
-
;;
|
|
95
|
-
sdd_discover|sdd_research|sdd_import_document|sdd_import_transcript|sdd_clarify|sdd_validate_ears)
|
|
96
|
-
# Research/clarify on spec branch
|
|
97
|
-
if [[ "$CURRENT_BRANCH" != spec/* ]]; then
|
|
98
|
-
warn "Research and clarification should be on a spec/NNN-* branch" "spec/NNN-feature-name"
|
|
99
|
-
fi
|
|
100
|
-
;;
|
|
101
|
-
sdd_verify_tests|sdd_verify_tasks|sdd_check_sync)
|
|
102
|
-
# Verification on spec branch
|
|
103
|
-
if [[ "$CURRENT_BRANCH" != spec/* ]]; then
|
|
104
|
-
warn "Verification should be on a spec/NNN-* branch" "spec/NNN-feature-name"
|
|
105
|
-
fi
|
|
106
|
-
;;
|
|
107
|
-
sdd_run_analysis|sdd_cross_analyze|sdd_compliance_check)
|
|
108
|
-
# Review on develop (after merge from spec)
|
|
109
|
-
if [[ "$CURRENT_BRANCH" != "develop" ]] && [[ "$CURRENT_BRANCH" != spec/* ]]; then
|
|
110
|
-
warn "Review tools typically run on 'develop' after merge from spec" "develop"
|
|
111
|
-
fi
|
|
112
|
-
;;
|
|
113
|
-
sdd_create_pr)
|
|
114
|
-
# PR: branch determines target
|
|
115
|
-
if [[ "$CURRENT_BRANCH" == spec/* ]]; then
|
|
116
|
-
echo " ℹ️ spec branch → PR will target develop"
|
|
117
|
-
elif [[ "$CURRENT_BRANCH" == "develop" ]]; then
|
|
118
|
-
echo " ℹ️ develop → PR will target stage"
|
|
119
|
-
elif [[ "$CURRENT_BRANCH" == "stage" ]]; then
|
|
120
|
-
echo " ℹ️ stage → PR will target main"
|
|
121
|
-
elif [[ "$CURRENT_BRANCH" == "main" ]]; then
|
|
122
|
-
warn "Cannot create PR from main — nothing to merge" "spec/*, develop, or stage"
|
|
123
|
-
fi
|
|
124
|
-
;;
|
|
125
|
-
*)
|
|
126
|
-
echo " ✅ No branch requirement for $TOOL"
|
|
127
|
-
;;
|
|
128
|
-
esac
|
|
34
|
+
MESSAGE="branch '$CURRENT_BRANCH' does not match persisted release policy '$EXPECTED' for $TOOL"
|
|
35
|
+
if [[ "$TOOL" == "Write" || "$TOOL" == "Edit" || "$TOOL" == "MultiEdit" ]] && [ "${SPECKY_GUARD:-advisory}" = "strict" ]; then
|
|
36
|
+
echo "🚫 [specky-branch-validator] BLOCKED — $MESSAGE" >&2
|
|
37
|
+
exit 2
|
|
38
|
+
fi
|
|
129
39
|
|
|
130
|
-
|
|
40
|
+
echo "⚠️ [specky-branch-validator] $MESSAGE" >&2
|
|
41
|
+
echo " Contract: $SPECKY_CONTRACT_ID" >&2
|
|
42
|
+
echo " Base: $SPECKY_BASE_BRANCH | Prefix: $SPECKY_BRANCH_PREFIX" >&2
|
|
43
|
+
echo " Enforce native edits with: export SPECKY_GUARD=strict" >&2
|
|
131
44
|
exit 0
|
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
# Paper: arXiv:2603.22106 — cognitive surrender measurement
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
9
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
10
|
+
specky_load_contract_context || exit $?
|
|
11
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
8
12
|
|
|
9
|
-
STATE
|
|
10
|
-
[ -z "$STATE" ] || [ ! -f "$STATE" ] && exit 0
|
|
13
|
+
STATE="$SPECKY_STATE_FILE"
|
|
11
14
|
|
|
12
15
|
# Check if gate history exists in state
|
|
13
16
|
if command -v jq &>/dev/null && [ -f "$STATE" ]; then
|
|
14
17
|
GATES=$(jq -r '.gate_history // [] | length' "$STATE" 2>/dev/null || echo "0")
|
|
15
|
-
LGTM_NO_MOD=$(jq -r '[.gate_history // [] | .[] | select(.
|
|
18
|
+
LGTM_NO_MOD=$(jq -r '[.gate_history // [] | .[] | select(.was_modified == false)] | length' "$STATE" 2>/dev/null || echo "0")
|
|
16
19
|
|
|
17
20
|
if [ "$GATES" -gt 0 ]; then
|
|
18
21
|
RATE=$((LGTM_NO_MOD * 100 / GATES))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Shared explicit feature-contract context for Specky hooks.
|
|
2
|
+
|
|
3
|
+
specky_load_contract_context() {
|
|
4
|
+
local context_dir context_exports
|
|
5
|
+
context_dir=$(cd "$(dirname "$0")" && pwd)
|
|
6
|
+
context_exports=$(node "$context_dir/specky-contract-context.mjs" shell) || return $?
|
|
7
|
+
eval "$context_exports"
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
specky_has_capability() {
|
|
11
|
+
local capability=",${SPECKY_CAPABILITIES:-},"
|
|
12
|
+
case "$capability" in
|
|
13
|
+
*",$1,"*) return 0 ;;
|
|
14
|
+
*) return 1 ;;
|
|
15
|
+
esac
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
specky_require_feature_context() {
|
|
19
|
+
specky_load_contract_context || return $?
|
|
20
|
+
if [ "${SPECKY_CONTEXT_ACTIVE:-0}" != "1" ]; then
|
|
21
|
+
echo "[specky] Explicit feature context required: ${SPECKY_CONTEXT_REASON:-missing context}." >&2
|
|
22
|
+
return 2
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
3
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
4
|
+
import { relative, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
function fail(message) {
|
|
7
|
+
console.error(`[specky-contract-context] ${message}`);
|
|
8
|
+
process.exit(2);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function readHookInput() {
|
|
12
|
+
const fromEnvironment = process.env.SDD_HOOK_INPUT;
|
|
13
|
+
if (fromEnvironment?.trim()) return fromEnvironment;
|
|
14
|
+
if (process.stdin.isTTY) return "";
|
|
15
|
+
try {
|
|
16
|
+
return readFileSync(0, "utf8");
|
|
17
|
+
} catch {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function parseInput(raw) {
|
|
23
|
+
if (!raw.trim()) return {};
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function objects(value) {
|
|
32
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return [];
|
|
33
|
+
const record = value;
|
|
34
|
+
return [
|
|
35
|
+
record,
|
|
36
|
+
record.tool_input,
|
|
37
|
+
record.toolInput,
|
|
38
|
+
record.arguments,
|
|
39
|
+
record.input,
|
|
40
|
+
record.params,
|
|
41
|
+
record.params?.arguments,
|
|
42
|
+
record.request?.params?.arguments,
|
|
43
|
+
].filter((candidate) => typeof candidate === "object" && candidate !== null && !Array.isArray(candidate));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function findString(input, keys) {
|
|
47
|
+
for (const candidate of objects(input)) {
|
|
48
|
+
for (const key of keys) {
|
|
49
|
+
const value = candidate[key];
|
|
50
|
+
if (typeof value === "string" && value.trim()) return value.trim();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function safeRelativePath(value, label) {
|
|
57
|
+
if (!value || value.startsWith("/") || value.startsWith("\\") || /^[A-Za-z]:/.test(value)) {
|
|
58
|
+
fail(`${label} must be a workspace-relative path.`);
|
|
59
|
+
}
|
|
60
|
+
const segments = value.split(/[\\/]/);
|
|
61
|
+
if (segments.includes("..") || value.includes("\0")) fail(`${label} contains unsafe traversal.`);
|
|
62
|
+
return segments.filter((segment) => segment && segment !== ".").join("/");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function canonicalize(value) {
|
|
66
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
67
|
+
if (typeof value !== "object" || value === null) return value;
|
|
68
|
+
return Object.keys(value)
|
|
69
|
+
.sort((left, right) => left.localeCompare(right))
|
|
70
|
+
.reduce((result, key) => {
|
|
71
|
+
result[key] = canonicalize(value[key]);
|
|
72
|
+
return result;
|
|
73
|
+
}, {});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function fingerprintContract(contract) {
|
|
77
|
+
const { fingerprint: _fingerprint, ...snapshot } = contract;
|
|
78
|
+
return createHash("sha256").update(JSON.stringify(canonicalize(snapshot))).digest("hex");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function safeEqualHex(left, right) {
|
|
82
|
+
if (!/^[a-f0-9]{64}$/.test(left) || !/^[a-f0-9]{64}$/.test(right)) return false;
|
|
83
|
+
return timingSafeEqual(Buffer.from(left, "hex"), Buffer.from(right, "hex"));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function shellQuote(value) {
|
|
87
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function resolveContext() {
|
|
91
|
+
const workspace = resolve(process.env.SPECKY_HOOK_WORKSPACE ?? process.cwd());
|
|
92
|
+
const input = parseInput(readHookInput());
|
|
93
|
+
const requestedSpecDir = process.env.SDD_SPEC_DIR ?? findString(input, ["spec_dir", "specDir"]);
|
|
94
|
+
const requestedFeatureNumber = process.env.SDD_FEATURE_NUMBER
|
|
95
|
+
?? findString(input, ["feature_number", "featureNumber"]);
|
|
96
|
+
|
|
97
|
+
if (!requestedSpecDir || !requestedFeatureNumber) {
|
|
98
|
+
return { active: false, reason: "missing explicit SDD_SPEC_DIR/SDD_FEATURE_NUMBER context" };
|
|
99
|
+
}
|
|
100
|
+
const specDir = safeRelativePath(requestedSpecDir, "spec_dir");
|
|
101
|
+
if (!/^\d{3}$/.test(requestedFeatureNumber)) fail("feature_number must be exactly three digits.");
|
|
102
|
+
|
|
103
|
+
const specRoot = resolve(workspace, specDir);
|
|
104
|
+
if (!existsSync(specRoot)) fail(`spec root does not exist: ${specDir}`);
|
|
105
|
+
const matches = readdirSync(specRoot, { withFileTypes: true })
|
|
106
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith(`${requestedFeatureNumber}-`));
|
|
107
|
+
if (matches.length !== 1) {
|
|
108
|
+
fail(`feature ${requestedFeatureNumber} must resolve to exactly one directory in ${specDir}; found ${matches.length}.`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const featureName = matches[0].name.slice(4);
|
|
112
|
+
const featureDirectory = `${specDir}/${matches[0].name}`;
|
|
113
|
+
const statePath = resolve(workspace, featureDirectory, ".sdd-state.json");
|
|
114
|
+
const signaturePath = `${statePath}.sig`;
|
|
115
|
+
if (!existsSync(statePath) || !existsSync(signaturePath)) {
|
|
116
|
+
fail(`feature ${requestedFeatureNumber} requires signed v5 state at ${featureDirectory}/.sdd-state.json.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const raw = readFileSync(statePath, "utf8");
|
|
120
|
+
let state;
|
|
121
|
+
try {
|
|
122
|
+
state = JSON.parse(raw);
|
|
123
|
+
} catch {
|
|
124
|
+
fail(`state is not valid JSON: ${relative(workspace, statePath)}`);
|
|
125
|
+
}
|
|
126
|
+
if (state.version !== "5.0.0") fail(`state version ${String(state.version)} requires migration.`);
|
|
127
|
+
|
|
128
|
+
const key = process.env.SDD_STATE_KEY
|
|
129
|
+
?? createHash("sha256").update(`specky-state-v1:${workspace}`).digest("hex");
|
|
130
|
+
const expectedSignature = createHmac("sha256", key).update(raw).digest("hex");
|
|
131
|
+
const storedSignature = readFileSync(signaturePath, "utf8").trim();
|
|
132
|
+
if (!safeEqualHex(storedSignature, expectedSignature)) fail("feature state signature verification failed.");
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
state.feature?.number !== requestedFeatureNumber ||
|
|
136
|
+
state.feature?.name !== featureName ||
|
|
137
|
+
state.feature?.directory !== featureDirectory
|
|
138
|
+
) {
|
|
139
|
+
fail(`feature state identity does not match ${featureDirectory}.`);
|
|
140
|
+
}
|
|
141
|
+
if (!state.contract || fingerprintContract(state.contract) !== state.contract.fingerprint) {
|
|
142
|
+
fail("feature contract fingerprint verification failed.");
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(state.contract.phases) || !state.contract.phases.includes(state.current_phase)) {
|
|
145
|
+
fail(`current phase ${String(state.current_phase)} is not enabled by the feature contract.`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const capabilities = Array.isArray(state.contract.capabilities) ? state.contract.capabilities : [];
|
|
149
|
+
const capabilityConfig = state.contract.capability_config ?? {};
|
|
150
|
+
const configuredCapabilities = Object.keys(capabilityConfig).sort();
|
|
151
|
+
const declaredCapabilities = [...new Set(capabilities)].sort();
|
|
152
|
+
if (JSON.stringify(configuredCapabilities) !== JSON.stringify(declaredCapabilities)) {
|
|
153
|
+
fail("capabilities and capability_config keys do not match.");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
active: true,
|
|
158
|
+
workspace,
|
|
159
|
+
specDir,
|
|
160
|
+
featureNumber: requestedFeatureNumber,
|
|
161
|
+
featureName,
|
|
162
|
+
featureDirectory,
|
|
163
|
+
statePath: relative(workspace, statePath).split(sep).join("/"),
|
|
164
|
+
phase: state.current_phase,
|
|
165
|
+
contract: state.contract,
|
|
166
|
+
gateDecision: state.gate_decision?.decision ?? "",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printShell(context) {
|
|
171
|
+
const contract = context.contract ?? {};
|
|
172
|
+
const capabilities = Array.isArray(contract.capabilities) ? contract.capabilities : [];
|
|
173
|
+
const release = contract.capability_config?.release;
|
|
174
|
+
const tdd = contract.capability_config?.tdd;
|
|
175
|
+
const compliance = contract.capability_config?.compliance;
|
|
176
|
+
const development = contract.capability_config?.["dev-environment"];
|
|
177
|
+
const iac = contract.capability_config?.iac;
|
|
178
|
+
const values = {
|
|
179
|
+
SPECKY_CONTEXT_ACTIVE: context.active ? "1" : "0",
|
|
180
|
+
SPECKY_CONTEXT_REASON: context.reason ?? "",
|
|
181
|
+
SPECKY_SPEC_DIR: context.specDir ?? "",
|
|
182
|
+
SPECKY_FEATURE_NUMBER: context.featureNumber ?? "",
|
|
183
|
+
SPECKY_FEATURE_NAME: context.featureName ?? "",
|
|
184
|
+
SPECKY_FEATURE_DIR: context.featureDirectory ?? "",
|
|
185
|
+
SPECKY_STATE_FILE: context.statePath ?? "",
|
|
186
|
+
SPECKY_PHASE: context.phase ?? "",
|
|
187
|
+
SPECKY_CONTRACT_ID: contract.id ?? "",
|
|
188
|
+
SPECKY_CONTRACT_FINGERPRINT: contract.fingerprint ?? "",
|
|
189
|
+
SPECKY_LIFECYCLE: contract.lifecycle ?? "",
|
|
190
|
+
SPECKY_WORKLOAD: contract.workload ?? "",
|
|
191
|
+
SPECKY_EXECUTION_MODE: contract.execution_mode ?? "",
|
|
192
|
+
SPECKY_CAPABILITIES: capabilities.join(","),
|
|
193
|
+
SPECKY_GATE_DECISION: context.gateDecision ?? "",
|
|
194
|
+
SPECKY_RELEASE_ENABLED: release ? "1" : "0",
|
|
195
|
+
SPECKY_BRANCH_PREFIX: release?.branch_prefix ?? "",
|
|
196
|
+
SPECKY_BASE_BRANCH: release?.base_branch ?? "",
|
|
197
|
+
SPECKY_CHECKPOINTS_REQUIRED: release?.checkpoints ? "1" : "0",
|
|
198
|
+
SPECKY_TDD_ENABLED: tdd ? "1" : "0",
|
|
199
|
+
SPECKY_TDD_THRESHOLD: tdd ? String(tdd.coverage_threshold) : "",
|
|
200
|
+
SPECKY_COMPLIANCE_ENABLED: compliance ? "1" : "0",
|
|
201
|
+
SPECKY_COMPLIANCE_FRAMEWORKS: compliance?.frameworks?.join(",") ?? "",
|
|
202
|
+
SPECKY_DEV_ENV_ENABLED: development ? "1" : "0",
|
|
203
|
+
SPECKY_DEV_LANGUAGE: development?.language ?? "",
|
|
204
|
+
SPECKY_IAC_ENABLED: iac ? "1" : "0",
|
|
205
|
+
SPECKY_IAC_PROVIDER: iac?.provider ?? "",
|
|
206
|
+
SPECKY_IAC_CLOUD: iac?.cloud ?? "",
|
|
207
|
+
};
|
|
208
|
+
for (const [key, value] of Object.entries(values)) console.log(`${key}=${shellQuote(value)}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const command = process.argv[2] ?? "shell";
|
|
212
|
+
const context = resolveContext();
|
|
213
|
+
if (command === "json") {
|
|
214
|
+
console.log(JSON.stringify(context));
|
|
215
|
+
} else if (command === "shell") {
|
|
216
|
+
printShell(context);
|
|
217
|
+
} else {
|
|
218
|
+
fail(`unknown command ${command}`);
|
|
219
|
+
}
|
|
@@ -5,14 +5,17 @@
|
|
|
5
5
|
# Paper: arXiv:2602.20478, arXiv:2603.22106
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
9
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
10
|
+
specky_load_contract_context || exit $?
|
|
11
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
8
12
|
|
|
9
|
-
LATEST
|
|
10
|
-
[ -z "$LATEST" ] && exit 0
|
|
13
|
+
LATEST="$SPECKY_FEATURE_DIR"
|
|
11
14
|
CONST="$LATEST/CONSTITUTION.md"
|
|
12
15
|
SPEC="$LATEST/SPECIFICATION.md"
|
|
13
16
|
[ -f "$CONST" ] && [ -f "$SPEC" ] || exit 0
|
|
14
17
|
|
|
15
|
-
echo "🧭 Drift Monitor: $(
|
|
18
|
+
echo "🧭 Drift Monitor: ${SPECKY_FEATURE_NUMBER}-${SPECKY_FEATURE_NAME} ($SPECKY_CONTRACT_ID)"
|
|
16
19
|
|
|
17
20
|
# Simple drift: compare key terms in constitution vs spec (POSIX ERE for portability)
|
|
18
21
|
CONST_TERMS=$(grep -oE '[A-Z][a-z]+([[:space:]][A-Z][a-z]+)+' "$CONST" 2>/dev/null | sort -u | wc -l)
|
|
@@ -10,9 +10,12 @@
|
|
|
10
10
|
# plain `set -eu` instead of bash-only `set -o pipefail`.
|
|
11
11
|
|
|
12
12
|
set -eu
|
|
13
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
14
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
15
|
+
specky_load_contract_context || exit $?
|
|
16
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
13
17
|
|
|
14
|
-
LATEST
|
|
15
|
-
[ -z "$LATEST" ] && exit 0
|
|
18
|
+
LATEST="$SPECKY_FEATURE_DIR"
|
|
16
19
|
SPEC="$LATEST/SPECIFICATION.md"
|
|
17
20
|
|
|
18
21
|
if [ ! -f "$SPEC" ] || [ ! -r "$SPEC" ]; then
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
# Summarizes artifact and prompts for LGTM before phase advancement
|
|
5
5
|
|
|
6
6
|
set -euo pipefail
|
|
7
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
8
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
9
|
+
specky_load_contract_context || exit $?
|
|
10
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
7
11
|
|
|
8
12
|
TOOL="${SDD_TOOL_NAME:-unknown}"
|
|
9
|
-
LATEST
|
|
13
|
+
LATEST="$SPECKY_FEATURE_DIR"
|
|
10
14
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
FEATURE=$(basename "$LATEST")
|
|
15
|
+
FEATURE="${SPECKY_FEATURE_NUMBER}-${SPECKY_FEATURE_NAME}"
|
|
14
16
|
|
|
15
17
|
print_summary() {
|
|
16
18
|
local file="$1" label="$2"
|
|
@@ -29,25 +31,25 @@ case "$TOOL" in
|
|
|
29
31
|
sdd_write_spec|sdd_turnkey_spec|sdd_figma_to_spec)
|
|
30
32
|
print_summary "SPECIFICATION.md" "SPECIFICATION.md"
|
|
31
33
|
echo ""
|
|
32
|
-
echo "⏸ LGTM
|
|
34
|
+
echo "⏸ LGTM REVIEW — Specify ($SPECKY_CONTRACT_ID)"
|
|
33
35
|
echo " Review SPECIFICATION.md above. Are the requirements correct?"
|
|
34
|
-
echo "
|
|
36
|
+
echo " If workspace policy requires LGTM, pass lgtm:true to sdd_advance_phase."
|
|
35
37
|
echo " Reply with feedback to revise."
|
|
36
38
|
;;
|
|
37
39
|
sdd_write_design)
|
|
38
40
|
print_summary "DESIGN.md" "DESIGN.md"
|
|
39
41
|
echo ""
|
|
40
|
-
echo "⏸ LGTM
|
|
42
|
+
echo "⏸ LGTM REVIEW — Design ($SPECKY_CONTRACT_ID)"
|
|
41
43
|
echo " Review DESIGN.md above. Is the architecture sound?"
|
|
42
|
-
echo "
|
|
44
|
+
echo " If workspace policy requires LGTM, pass lgtm:true to sdd_advance_phase."
|
|
43
45
|
echo " Reply with feedback to revise."
|
|
44
46
|
;;
|
|
45
47
|
sdd_write_tasks)
|
|
46
48
|
print_summary "TASKS.md" "TASKS.md"
|
|
47
49
|
echo ""
|
|
48
|
-
echo "⏸ LGTM
|
|
50
|
+
echo "⏸ LGTM REVIEW — Tasks ($SPECKY_CONTRACT_ID)"
|
|
49
51
|
echo " Review TASKS.md above. Is the task breakdown complete?"
|
|
50
|
-
echo "
|
|
52
|
+
echo " If workspace policy requires LGTM, pass lgtm:true to sdd_advance_phase."
|
|
51
53
|
echo " Reply with feedback to revise."
|
|
52
54
|
;;
|
|
53
55
|
*)
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
# Paper: arXiv:2507.09089 — AI productivity paradox
|
|
6
6
|
|
|
7
7
|
set -euo pipefail
|
|
8
|
+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
|
9
|
+
. "$SCRIPT_DIR/specky-contract-context.bash"
|
|
10
|
+
specky_load_contract_context || exit $?
|
|
11
|
+
[ "${SPECKY_CONTEXT_ACTIVE:-0}" = "1" ] || exit 0
|
|
8
12
|
|
|
9
|
-
LATEST
|
|
10
|
-
[ -z "$LATEST" ] && exit 0
|
|
13
|
+
LATEST="$SPECKY_FEATURE_DIR"
|
|
11
14
|
|
|
12
15
|
# Check if analysis is done but metrics not generated
|
|
13
16
|
if [ -f "$LATEST/ANALYSIS.md" ] && [ ! -f "$LATEST/specky-metrics-dashboard.html" ]; then
|