Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,26 @@ kind-setup-openshell-cli: check-kubectl _kind-require-cluster ## Auto-discover t
echo "$(COLOR_BLUE)▶$(COLOR_RESET) Found openshell-gateway in: $$NAMESPACES"; \
./scripts/setup-gateway-cli.sh $$NAMESPACES

kind-stop-openshell-cli: ## Stop openshell gateway port-forwards
@STOPPED=0; \
for pidfile in $(KIND_PF_DIR)/openshell-pf-*.pid; do \
[ -f "$$pidfile" ] || continue; \
NS=$$(basename "$$pidfile" .pid | sed 's/^openshell-pf-//'); \
PID=$$(cat "$$pidfile"); \
if ps -p "$$PID" >/dev/null 2>&1; then \
kill "$$PID" 2>/dev/null || true; \
echo " Stopped openshell port-forward for $$NS (PID $$PID)"; \
STOPPED=1; \
fi; \
rm -f "$$pidfile"; \
rm -f "$(KIND_PF_DIR)/openshell-pf-$$NS.log"; \
done; \
if [ "$$STOPPED" -eq 1 ]; then \
echo "$(COLOR_GREEN)✓$(COLOR_RESET) Openshell port-forwards stopped"; \
else \
echo "$(COLOR_YELLOW)No active openshell port-forwards found$(COLOR_RESET)"; \
fi

kind-clean: kind-down ## Alias for kind-down

e2e-clean: kind-down ## Alias for kind-down (backward compatibility)
Expand Down
29 changes: 14 additions & 15 deletions scripts/setup-gateway-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@ set -e

NAMESPACES=("${@:-tenant-a}")
CERT_BASE="$HOME/.config/openshell/gateways"
PF_DIR="/tmp/ambient-code"
PF_PIDS=()
GW_PORTS=()

cleanup() {
for pid in "${PF_PIDS[@]}"; do
kill "$pid" 2>/dev/null || true
done
}
trap cleanup EXIT
mkdir -p "$PF_DIR"

for pidfile in "$PF_DIR"/openshell-pf-*.pid; do
[ -f "$pidfile" ] || continue
OLD_PID=$(cat "$pidfile")
kill "$OLD_PID" 2>/dev/null || true
rm -f "$pidfile"
done

for NS in "${NAMESPACES[@]}"; do
GW_NAME="$NS"
Expand All @@ -54,15 +57,15 @@ for NS in "${NAMESPACES[@]}"; do

# Start port-forward on :0 (kernel picks a free port), capture the assigned port
kubectl port-forward -n "$NS" statefulset/openshell-gateway ":8080" \
>/tmp/pf-${NS}.log 2>&1 &
>"$PF_DIR/openshell-pf-${NS}.log" 2>&1 &
PF_PID=$!
PF_PIDS+=($PF_PID)

# Wait for kubectl to print the assigned port
PORT=""
for attempt in $(seq 1 30); do
if [ -s "/tmp/pf-${NS}.log" ]; then
PORT=$(grep -oE 'Forwarding from 127\.0\.0\.1:[0-9]+' "/tmp/pf-${NS}.log" | grep -oE '[0-9]+$' | head -1)
if [ -s "$PF_DIR/openshell-pf-${NS}.log" ]; then
PORT=$(grep -oE 'Forwarding from 127\.0\.0\.1:[0-9]+' "$PF_DIR/openshell-pf-${NS}.log" | grep -oE '[0-9]+$' | head -1)
if [ -n "$PORT" ]; then
break
fi
Expand All @@ -77,6 +80,7 @@ for NS in "${NAMESPACES[@]}"; do
continue
fi

echo "$PF_PID" > "$PF_DIR/openshell-pf-${NS}.pid"
GW_PORTS+=("$PORT")

# Register the gateway. Remove first if it already exists.
Expand Down Expand Up @@ -143,9 +147,4 @@ for NS in "${NAMESPACES[@]}"; do
done
echo ""
echo "Port-forwards are running in the background (PIDs: ${PF_PIDS[*]})."
echo "Press Ctrl-C to stop them, or run: kill ${PF_PIDS[*]}"
echo ""

# Keep port-forwards alive until interrupted
trap - EXIT
wait
echo "Stop with: make kind-stop-openshell-cli"
137 changes: 137 additions & 0 deletions skills/review/spec-gap-analysis/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
name: spec-gap-analysis
description: >
Validate a spec against the actual codebase. Reads a *.spec.md file, searches
for its implementation and test coverage, then writes a *.spec-gaps.md report
alongside it. Run this on every spec before a release, after major refactors,
or when you suspect spec drift. Triggers on: "gap analysis", "spec gaps",
"validate spec", "spec coverage", "what's missing", "audit spec", "find gaps",
"spec vs implementation", "untested requirements", "spec drift".
---

# Spec Gap Analysis

Validate one spec file against implementation and tests. Writes a sibling `*.spec-gaps.md`.

## Usage

```text
/spec-gap-analysis specs/platform/control-plane.spec.md
/spec-gap-analysis specs/security/rbac-enforcement.spec.md
```

## User Input

```text
$ARGUMENTS
```

## Steps

### Phase 1 — Parse the Spec

Read the spec file. Extract every requirement (statements using SHALL, MUST, SHOULD, MAY) and every scenario (GIVEN/WHEN/THEN blocks). Assign IDs: R1, R2, ... for requirements; S1, S2, ... for scenarios. Note which components the spec touches — use `CLAUDE.md` `## Structure` to map domains to source directories.

Summarize: "This spec covers N requirements and M scenarios across components X, Y, Z."

### Phase 2 — Search the Implementation

For each requirement, grep the codebase for key terms (function names, env vars, API paths, K8s resource kinds, error strings). Read matching files. Classify each requirement:

- **Implemented** — code matches the spec
- **Partial** — some aspects present, others missing
- **Deviation** — implemented differently than spec describes
- **Missing** — no implementation found

Also note **Impl Extras** — implementation behavior the spec doesn't cover.

### Phase 3 — Search the Tests

For each requirement, grep test directories for the same terms. Read matching test files. Classify coverage:

- **Full** — all scenarios have corresponding tests
- **Partial** — some scenarios tested
- **None** — no tests found

### Phase 4 — Classify Gaps

Produce four gap types:

- **G-type (Implementation Gap)** — spec requirement with no or partial implementation
- **T-type (Test Gap)** — implemented but untested. Priority: CRITICAL for security, HIGH for core paths, MEDIUM for secondary, LOW for edge cases
- **D-type (Drift)** — spec and implementation disagree. Needs human decision: update spec or fix code
- **E-type (Undocumented)** — implementation exists but spec doesn't describe it. Note for spec update consideration

### Phase 5 — Propose Tests

For each T-type gap, propose concrete test names and what they validate. Group by test file. Follow the project's existing test conventions.

### Phase 6 — Write the Report

Write to `<spec-path>.replace('.spec.md', '.spec-gaps.md')`. Follow the output format below.

## Output Format

```markdown
# <Spec Title> — Gap Analysis

**Date:** YYYY-MM-DD
**Spec:** `<path>`
**Components:** <source dirs>
**Tests:** <test dirs>

---

## Methodology
<brief description>

## Requirement Coverage Matrix

< /dev/null | ID | Requirement | Impl Status | Test Coverage | Priority |
|----|-------------|-------------|---------------|----------|

## Implementation Gaps (G-type)

### G1: <title>
- **Requirement:** R<n>
- **Spec says:** <quote>
- **Current state:** <description>
- **Risk:** <impact>

## Test Gaps (T-type)

### T1: <title>
- **Requirement:** R<n>
- **Implementation:** <file:line>
- **Current tests:** None / partial
- **Risk:** CRITICAL / HIGH / MEDIUM / LOW
- **Proposed tests:** `test_<name>` — <validates what>

## Drift (D-type)

### D1: <title>
- **Spec says:** <quote>
- **Implementation does:** <actual behavior>
- **Resolution:** Update spec / Fix code

## Undocumented Behavior (E-type)

### E1: <title>
- **Implementation:** <file:line>
- **Behavior:** <description>
- **Recommendation:** Add to spec / Remove / Keep internal

## Summary
- **Requirements:** N total (N impl, N partial, N missing)
- **Test coverage:** N full, N partial, N none
- **Top risks:** <ranked list>
```

## Heuristics

- Security requirements (auth, RBAC, tokens, paths) are always CRITICAL priority.
- SHALL/MUST with no test = T-type gap, always.
- Deviations need human judgment — flag, don't fix.
- Read actual code. Never guess from file names.
- One spec at a time. Run repeatedly for the full suite.
- Favor explanation over rigidity — if a requirement is ambiguous, say so rather than forcing a classification.
47 changes: 47 additions & 0 deletions skills/review/spec-gap-analysis/evals/evals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
[
{
"input": "run a gap analysis on specs/platform/control-plane.spec.md",
"expected_tool_call": "Skill",
"expected_args": {
"skill": "spec-gap-analysis",
"args": "specs/platform/control-plane.spec.md"
},
"description": "Triggers gap analysis on a specific platform spec"
},
{
"input": "what gaps exist in the runner spec?",
"expected_tool_call": "Skill",
"expected_args": {
"skill": "spec-gap-analysis",
"args": "specs/platform/runner.spec.md"
},
"description": "Triggers gap analysis when user asks about spec coverage"
},
{
"input": "validate spec coverage for RBAC enforcement",
"expected_tool_call": "Skill",
"expected_args": {
"skill": "spec-gap-analysis",
"args": "specs/security/rbac-enforcement.spec.md"
},
"description": "Triggers gap analysis for a security spec"
},
{
"input": "find untested requirements in the SSO spec",
"expected_tool_call": "Skill",
"expected_args": {
"skill": "spec-gap-analysis",
"args": "specs/security/sso-authentication.spec.md"
},
"description": "Triggers gap analysis focused on test coverage"
},
{
"input": "spec gaps for the UI architecture",
"expected_tool_call": "Skill",
"expected_args": {
"skill": "spec-gap-analysis",
"args": "specs/ui/architecture.spec.md"
},
"description": "Triggers gap analysis for a UI spec"
}
]
Loading