Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ jobs:
test -d /tmp/case/resources/speaking/showcase-demo
node skills/petition-portal/scripts/build-portal.mjs /tmp/case -o /tmp/packet.html
test -s /tmp/packet.html
- name: Add another petition workspace to an existing case
run: bash scripts/test-scaffold.sh
- name: Similarity check catches near-duplicate letters
run: |
mkdir -p /tmp/l
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ wrappers over the skills; the logic lives in the skills themselves.

| Command | |
|---|---|
| `/new-case <dir> [o1a\|eb1a\|both]` | Scaffold a case workspace |
| `/new-case <dir> [o1a\|eb1a\|both]` | Scaffold a case workspace, or add another petition type to an existing one |
| `/triage` | Interview-driven criteria feasibility assessment |
| `/status` | Stage, blockers, and prioritized next actions |
| `/add-evidence` | File a document and record it in one step |
Expand Down
3 changes: 3 additions & 0 deletions commands/new-case.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Arguments: `$ARGUMENTS` — expected as `<target-dir> [o1a|eb1a|both]`, defaulti
2. Run its scaffold script:
`${CLAUDE_PLUGIN_ROOT}/skills/petition-workspace/scripts/scaffold-case.sh <target-dir> <type>`
It refuses to write into a non-empty directory — do not work around that; ask the user.
If the directory is already a case and the user wants another petition type alongside
it, use `scaffold-case.sh --add <target-dir> <type>` instead — it adds only the new
petition workspace and leaves everything else untouched.
3. Then help fill in `case-overview.md`, starting with the field of endeavor. Push for a
narrow, specific field statement; it is the highest-leverage line in the whole case.
4. Remind the user once that this workspace will hold sensitive records and should not go
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"scripts": {
"validate": "node scripts/validate-skills.mjs",
"check-pii": "node scripts/check-pii.mjs",
"test": "npm run validate && npm run check-pii"
"test": "npm run validate && npm run check-pii && npm run test-scaffold",
"test-scaffold": "bash scripts/test-scaffold.sh"
},
"devDependencies": {
"gray-matter": "^4.0.3",
Expand Down
55 changes: 55 additions & 0 deletions scripts/test-scaffold.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env bash
# Exercise scaffold-case.sh end to end: fresh scaffold, and --add for another
# petition workspace on an existing case.
# Run: bash scripts/test-scaffold.sh
set -euo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SCAFFOLD="$ROOT/skills/petition-workspace/scripts/scaffold-case.sh"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
fails=0

ok() { echo " ok $1"; }
fail() { echo " FAIL $1" >&2; fails=$((fails + 1)); }
check() { local name="$1"; shift; if "$@" >/dev/null 2>&1; then ok "$name"; else fail "$name"; fi; }
refuses() { local name="$1"; shift; if "$@" >/dev/null 2>&1; then fail "$name"; else ok "$name"; fi; }
criteria() { grep -c '^### Criterion' "$1"; }

echo "scaffold-case.sh"

case="$TMP/case"
"$SCAFFOLD" "$case" o1a >/dev/null
check "fresh o1a scaffold has 8 criteria" test "$(criteria "$case/o1a/evidence-list.md")" = 8
check "fresh o1a scaffold has no eb1a/" test ! -e "$case/eb1a"
refuses "plain scaffold still refuses a non-empty directory" "$SCAFFOLD" "$case" eb1a

# Simulate a case in progress: user edits that --add must not touch.
echo "Field of endeavor: industrial robotics" >> "$case/case-overview.md"
echo "| | Org chart | [x] collected | resources/company-docs/org-chart.png |" >> "$case/o1a/evidence-list.md"
mkdir -p "$case/resources/awards/meridian" && echo x > "$case/resources/awards/meridian/cert.pdf"
before="$(cd "$case" && find case-overview.md README.md .gitignore resources o1a -type f -exec shasum {} + | sort)"

"$SCAFFOLD" --add "$case" eb1a >/dev/null
check "--add eb1a creates eb1a/ with 10 criteria" test "$(criteria "$case/eb1a/evidence-list.md")" = 10
check "--add eb1a writes 10 criterion argument files" test "$(ls "$case/eb1a/evidence-by-criterion" | wc -l | tr -d ' ')" = 10
check "--add eb1a writes overview and narrative" test -s "$case/eb1a/overview.md" -a -s "$case/eb1a/petition-narrative.md"
after="$(cd "$case" && find case-overview.md README.md .gitignore resources o1a -type f -exec shasum {} + | sort)"
check "--add leaves existing case files untouched" test "$before" = "$after"

refuses "--add refuses an existing petition workspace" "$SCAFFOLD" --add "$case" eb1a
refuses "--add both refuses when either already exists" "$SCAFFOLD" --add "$case" both
refuses "--add refuses a directory that is not a case" "$SCAFFOLD" --add "$TMP/nope" o1a
refuses "--add requires a petition type" "$SCAFFOLD" --add "$case"
refuses "--add rejects an unknown petition type" "$SCAFFOLD" --add "$case" h1b

# The added workspace must work with the rest of the toolchain.
printf 'x' > "$TMP/f.png"
check "add-evidence files into the added workspace" \
node "$ROOT/skills/petition-workspace/scripts/add-evidence.mjs" "$case" -p eb1a -c 7 -d "Showcase demo" "$TMP/f.png"
check "added workspace's evidence-list records the exhibit" grep -q "Showcase demo" "$case/eb1a/evidence-list.md"
check "review packet builds over both workspaces" \
node "$ROOT/skills/petition-portal/scripts/build-portal.mjs" "$case" -o "$TMP/packet.html"

if [ "$fails" -gt 0 ]; then echo "$fails failure(s)" >&2; exit 1; fi
echo "test-scaffold: all checks passed"
13 changes: 13 additions & 0 deletions skills/petition-workspace/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ scripts/scaffold-case.sh <target-dir> [o1a|eb1a|both]
Refuses to write into a non-empty directory. Creates `resources/` for raw material,
one directory per petition type, `communications/`, and `case-overview.md`.

## Add another petition workspace to an existing case

```bash
scripts/scaffold-case.sh --add <case-dir> <o1a|eb1a|both>
```

For a case that started as an O-1A and is now also pursuing an EB-1A (or the reverse).
Writes only the new petition directory — `resources/`, `case-overview.md`, and any
existing petition directory are left untouched. Refuses if the directory is not a case
(no `case-overview.md`) or the petition directory already exists. Afterwards, update
*Petition type(s)* in `case-overview.md`; the new workspace numbers its exhibits
independently and draws on the same shared `resources/`.

## File evidence with the tool, not by hand

```bash
Expand Down
42 changes: 37 additions & 5 deletions skills/petition-workspace/scripts/scaffold-case.sh
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
#!/usr/bin/env bash
# Scaffold a petition case workspace.
# Usage: scaffold-case.sh <target-dir> [o1a|eb1a|both]
# scaffold-case.sh --add <case-dir> <o1a|eb1a|both>
#
# --add adds another petition workspace to an existing case (an O-1A case later
# pursuing an EB-1A). It writes only the missing petition directories and never
# touches resources/, case-overview.md, or a petition directory that already exists.
set -euo pipefail

TARGET="${1:?usage: scaffold-case.sh <target-dir> [o1a|eb1a|both]}"
TYPE="${2:-o1a}"
USAGE="usage: scaffold-case.sh <target-dir> [o1a|eb1a|both] | scaffold-case.sh --add <case-dir> <o1a|eb1a|both>"
ADD=0
if [ "${1:-}" = "--add" ]; then ADD=1; shift; fi

TARGET="${1:?$USAGE}"
if [ "$ADD" = 1 ]; then TYPE="${2:?$USAGE}"; else TYPE="${2:-o1a}"; fi
TPL="$(cd "$(dirname "$0")/../templates/case" && pwd)"

case "$TYPE" in o1a|eb1a|both) ;; *) echo "type must be o1a, eb1a, or both" >&2; exit 2 ;; esac

if [ -e "$TARGET" ] && [ -n "$(ls -A "$TARGET" 2>/dev/null)" ]; then
if [ "$ADD" = 1 ]; then
if [ ! -f "$TARGET/case-overview.md" ]; then
echo "not a case workspace (no case-overview.md): $TARGET — scaffold it without --add" >&2
exit 1
fi
elif [ -e "$TARGET" ] && [ -n "$(ls -A "$TARGET" 2>/dev/null)" ]; then
echo "refusing to scaffold into non-empty directory: $TARGET" >&2
echo "to add another petition workspace to an existing case: scaffold-case.sh --add $TARGET <o1a|eb1a>" >&2
exit 1
fi

mkdir -p "$TARGET"/{resources/{awards,membership,press,judging,patents,publications,compensation,company-docs,recommendation-letters,education,speaking,photos},communications/{attorneys,recommenders},reference}

petitions=()
case "$TYPE" in
o1a) petitions=(o1a) ;;
eb1a) petitions=(eb1a) ;;
both) petitions=(o1a eb1a) ;;
esac

if [ "$ADD" = 1 ]; then
for p in "${petitions[@]}"; do
if [ -e "$TARGET/$p" ]; then
echo "refusing to overwrite existing petition workspace: $TARGET/$p" >&2
exit 1
fi
done
fi

# Idempotent: on --add this only fills in shared folders an older case is missing.
mkdir -p "$TARGET"/{resources/{awards,membership,press,judging,patents,publications,compensation,company-docs,recommendation-letters,education,speaking,photos},communications/{attorneys,recommenders},reference}

# Criterion names per petition type. O-1A has 8; EB-1A has 10, and the numbering
# diverges from criterion 7 onward — do not share one list between them.
o1a_criteria=(
Expand Down Expand Up @@ -65,6 +90,13 @@ for p in "${petitions[@]}"; do
"$(echo "$p" | tr '[:lower:]' '[:upper:]')" > "$dir/petition-narrative.md"
done

if [ "$ADD" = 1 ]; then
echo "added ${petitions[*]} petition workspace to case at $TARGET"
echo "update 'Petition type(s)' in case-overview.md; resources/ is shared, exhibit numbering is independent"
for p in "${petitions[@]}"; do find "$TARGET/$p" -type f | sort | sed "s|^$TARGET|.|"; done
exit 0
fi

cat > "$TARGET/case-overview.md" <<'EOF'
# Case Overview

Expand Down
Loading