Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f79efae
Adventure draft: πŸ§ͺ Side Effects May Vary (OpenFeature + flagd)
aepfli Apr 25, 2026
155fe5a
Drop DinD; sibling-service devcontainers for Intermediate + Expert
aepfli Apr 25, 2026
f4326eb
docs(intermediate): mention IN_PROCESS resolver against the flagd sib…
aepfli Apr 25, 2026
9582612
ux(vscode): per-level launch.json + tasks.json; nudge F5 in the docs
aepfli Apr 26, 2026
268eef8
docs: lean on Spring Boot Dashboard for Run UX, drop checked-in .vscode
aepfli Apr 26, 2026
e6e9b8e
docs+ux: add concept primers to Intermediate + Expert; pre-open more …
aepfli Apr 26, 2026
90a20d4
narrative: race=query / country=env, rename DemoApplication+IndexCont…
aepfli Apr 26, 2026
4844893
ux: materialize .vscode/launch.json + tasks.json at codespace boot
aepfli Apr 26, 2026
63a8460
ux: check in .vscode/ run configs per level, scoped via per-scenario …
aepfli Apr 26, 2026
4de2376
ux(post-start): refresh banners + open files via `code` (compose-base…
aepfli Apr 26, 2026
a2997d9
narrative: invocation-context dose, with Expert ContextSpanHook task
aepfli Apr 26, 2026
4629186
docs(expert): PII allowlist warning on the ContextSpanHook task
aepfli Apr 26, 2026
d0eac1a
narrative: CustomHook becomes a real audit log; Expert adds it on top…
aepfli Apr 26, 2026
6e6acf5
rename: CustomHook -> AuditHook to match its actual job
aepfli Apr 26, 2026
144dd70
fix(verify): grammar β€” "An AuditHook"
aepfli Apr 26, 2026
1807ba9
beginner: wire flagd as a sibling so RPC mode is the level-1 shape
aepfli Apr 26, 2026
41d425e
fix(flagd ports): correct the labels β€” 8014 is management, 8016 is OFREP
aepfli Apr 26, 2026
9fa52df
docs(solutions): close gaps the participant would walk into
aepfli Apr 27, 2026
f11f400
narrative: targeting changes the result, not the trial
aepfli Apr 27, 2026
a75b39e
rename: race -> species (and RaceInterceptor -> SpeciesInterceptor)
aepfli Apr 27, 2026
ea58844
docs(intermediate): land six review-feedback fixes in one pass
aepfli Apr 27, 2026
a550797
fix(devcontainer): install jq + prefix tracking-context tag
aepfli Apr 27, 2026
c7c7149
fix(intermediate): broken-state Trial method = observeSubject (not he…
aepfli Apr 27, 2026
7b11f13
docs(expert): targetingKey wiring is a stated task; ContextSpanHook c…
aepfli Apr 27, 2026
32d9646
docs: density trim across the adventure (~140 lines, ~20% net)
aepfli Apr 27, 2026
f2085e5
docs: Intermediate now teaches userId/targetingKey wiring; "carried o…
aepfli Apr 27, 2026
4206c25
rename: Side Effects May Vary β†’ Blind by Design
aepfli Apr 30, 2026
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
36 changes: 36 additions & 0 deletions .devcontainer/00-blind-by-design_01-beginner/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "πŸ§ͺ Adventure 00 | 🟒 Beginner (Stand up the lab)",
"dockerComposeFile": "docker-compose.yml",
"service": "workspace",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/adventures/planned/00-blind-by-design/beginner",
"postCreateCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_01-beginner/post-create.sh",
"postStartCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_01-beginner/post-start.sh",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"vmware.vscode-spring-boot",
"vscjava.vscode-spring-boot-dashboard",
"redhat.vscode-xml"
]
},
"codespaces": {
"openFiles": [
"adventures/planned/00-blind-by-design/docs/beginner.md",
"adventures/planned/00-blind-by-design/beginner/src/main/java/dev/openfeature/demo/java/demo/Trial.java",
"adventures/planned/00-blind-by-design/beginner/flags.json"
]
}
},
"forwardPorts": [8080, 8013, 8014, 8015, 8016],
"portsAttributes": {
"8080": { "label": "Lab (Spring Boot)", "onAutoForward": "notify" },
"8013": { "label": "flagd gRPC eval", "onAutoForward": "ignore" },
"8014": { "label": "flagd management/metrics", "onAutoForward": "ignore" },
"8015": { "label": "flagd sync (IN_PROCESS)", "onAutoForward": "ignore" },
"8016": { "label": "flagd OFREP", "onAutoForward": "ignore" }
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
}
}
40 changes: 40 additions & 0 deletions .devcontainer/00-blind-by-design_01-beginner/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Multi-container devcontainer for Beginner. The lab itself runs in
# `workspace`; flagd runs as a sibling so participants meet the realistic
# shape ("the SDK talks RPC to a separate flag service") from level 1
# instead of running file-mode in-process and throwing it away for
# Intermediate.
#
# Both services bind-mount the same workspace at the same path. flagd
# watches the participant's flags.json directly β€” edit it in the IDE,
# the file watcher reloads, the next request sees the new variant.

services:
workspace:
image: mcr.microsoft.com/devcontainers/java:1-21
volumes:
- ../..:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}:cached
command: sleep infinity
environment:
# The flagd Java provider reads these env vars by default when
# FlagdOptions.builder() is invoked without an explicit host/port.
# Pre-set so a vanilla `Resolver.RPC` config Just Works inside the
# devcontainer β€” and a participant who runs the lab from their host
# machine after `docker compose up flagd` will hit `localhost:8013`
# via the published port.
- FLAGD_HOST=flagd
- FLAGD_PORT=8013

flagd:
image: ghcr.io/open-feature/flagd:latest
container_name: side-effects-beginner-flagd
volumes:
- ../..:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}:ro
command:
- start
- --uri
- file:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}/adventures/planned/00-blind-by-design/beginner/flags.json
ports:
- "8013:8013"
- "8014:8014"
- "8015:8015"
- "8016:8016"
28 changes: 28 additions & 0 deletions .devcontainer/00-blind-by-design_01-beginner/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CHALLENGE_DIR="$REPO_ROOT/adventures/planned/00-blind-by-design/beginner"

# shellcheck disable=SC1091
source "$REPO_ROOT/lib/scripts/tracker.sh"
set_tracking_context "00-blind-by-design" "beginner"
track_codespace_created

# Install gum (used by the verify.sh output helpers).
"$REPO_ROOT/lib/shared/init.sh" --version v0.17.0 # https://github.com/charmbracelet/gum/releases

# jq is needed by verify.sh; the Java devcontainer image is debian-based.
if ! command -v jq >/dev/null 2>&1; then
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends jq
fi

# Java 21 is provided by the devcontainer image (mcr.microsoft.com/devcontainers/java:1-21-bullseye).
# Pre-fetch Maven dependencies so the IDE is responsive immediately.
echo "✨ Resolving Maven dependencies for the lab..."
cd "$CHALLENGE_DIR"
chmod +x ./mvnw
./mvnw -q -B -DskipTests dependency:go-offline || true

echo "βœ… Post-create complete."
51 changes: 51 additions & 0 deletions .devcontainer/00-blind-by-design_01-beginner/post-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -e

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CHALLENGE_DIR="$REPO_ROOT/adventures/planned/00-blind-by-design/beginner"

cat <<EOF

✨ Adventure 00 β€” Level 1 (🟒 Beginner): Stand up the lab

πŸ“‚ Challenge directory:
$CHALLENGE_DIR

🧬 A flagd sidecar is already running next to your workspace
- gRPC eval :8013 (this is what your FlagdProvider will talk to)
- management :8014 (Prometheus metrics + /healthz, /readyz)
- sync :8015 (used in the Intermediate IN_PROCESS sidebar)
- OFREP :8016 (HTTP eval API, handy for poking flagd directly)
FLAGD_HOST=flagd is exported into this shell, so a default
Resolver.RPC config picks the sidecar up automatically.

β–Ά Run the lab β€” one launch config in .vscode/launch.json:
πŸ§ͺ Run the Lab
Open the Run and Debug view (Ctrl/Cmd + Shift + D) and hit β–Ά.

Or from the terminal:
./mvnw spring-boot:run

πŸ‘‰ In another terminal, hit it:
curl -s http://localhost:8080/ | jq

βœ… Run the verification when you're ready:
./verify.sh
or use the πŸ§ͺ Verify Solution task: Tasks β†’ Run Test Task.

EOF

# Track that the environment is ready.
# shellcheck disable=SC1091
source "$REPO_ROOT/lib/scripts/tracker.sh"
track_codespace_initialized

# Open the relevant files in the connected editor. customizations.codespaces.openFiles
# is unreliable for dockerComposeFile-based devcontainers (the orchestrator merges
# devcontainer.json and the field is sometimes dropped). `code` is the same CLI the
# editor uses internally and works against either the web or desktop client.
if command -v code >/dev/null 2>&1; then
code "$REPO_ROOT/adventures/planned/00-blind-by-design/docs/beginner.md" \
"$CHALLENGE_DIR/src/main/java/dev/openfeature/demo/java/demo/Trial.java" \
2>/dev/null || true
fi
35 changes: 35 additions & 0 deletions .devcontainer/00-blind-by-design_02-intermediate/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "πŸ§ͺ Adventure 00 | 🟑 Intermediate (Outcome by cohort)",
"dockerComposeFile": "docker-compose.yml",
"service": "workspace",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/adventures/planned/00-blind-by-design/intermediate",
"postCreateCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_02-intermediate/post-create.sh",
"postStartCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_02-intermediate/post-start.sh",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"vmware.vscode-spring-boot",
"vscjava.vscode-spring-boot-dashboard"
]
},
"codespaces": {
"openFiles": [
"adventures/planned/00-blind-by-design/docs/intermediate.md",
"adventures/planned/00-blind-by-design/intermediate/src/main/java/dev/openfeature/demo/java/demo/OpenFeatureConfig.java",
"adventures/planned/00-blind-by-design/intermediate/flags.json"
]
}
},
"forwardPorts": [8080, 8013, 8014, 8015, 8016],
"portsAttributes": {
"8080": { "label": "Lab (Spring Boot)", "onAutoForward": "notify" },
"8013": { "label": "flagd gRPC eval", "onAutoForward": "ignore" },
"8014": { "label": "flagd management/metrics", "onAutoForward": "ignore" },
"8015": { "label": "flagd sync (IN_PROCESS)", "onAutoForward": "ignore" },
"8016": { "label": "flagd OFREP", "onAutoForward": "ignore" }
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Multi-container devcontainer for Intermediate. The lab itself runs in
# `workspace`; flagd runs as a sibling so participants who finish the
# FILE-mode path early can flip the FlagdProvider to RPC mode and talk
# to flagd:8013 without any Docker-in-Docker dance.
#
# Both services bind-mount the same workspace at the same path. flagd
# watches the participant's flags.json directly β€” edit it in the IDE,
# the file watcher reloads.

services:
workspace:
image: mcr.microsoft.com/devcontainers/java:1-21
volumes:
- ../..:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}:cached
command: sleep infinity
environment:
# Pre-set FLAGD_HOST so participants who switch to RPC mode in
# OpenFeatureConfig do not have to hard-code the hostname.
- FLAGD_HOST=flagd
- FLAGD_PORT=8013
# The trial's country of registration. Used by OpenFeatureConfig
# via System.getenv("COUNTRY") to populate the global eval context.
# `de` by default so F5 / Spring Boot Dashboard runs already exercise
# the country-targeting branch. Override with run-austria.sh or
# `COUNTRY=at ./mvnw spring-boot:run` to flip.
- COUNTRY=de

flagd:
image: ghcr.io/open-feature/flagd:latest
container_name: side-effects-intermediate-flagd
volumes:
- ../..:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}:ro
command:
- start
- --uri
- file:/workspaces/${localWorkspaceFolderBasename:-open-ecosystem-challenges}/adventures/planned/00-blind-by-design/intermediate/flags.json
ports:
- "8013:8013"
- "8014:8014"
- "8015:8015"
- "8016:8016"
30 changes: 30 additions & 0 deletions .devcontainer/00-blind-by-design_02-intermediate/post-create.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -e

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"

# shellcheck disable=SC1091
source "$REPO_ROOT/lib/scripts/tracker.sh"
set_tracking_context "00-blind-by-design" "intermediate"
track_codespace_created

"$REPO_ROOT/lib/shared/init.sh" --version v0.17.0 # https://github.com/charmbracelet/gum/releases

# jq is needed by verify.sh; the Java devcontainer image is debian-based.
if ! command -v jq >/dev/null 2>&1; then
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends jq
fi

CHALLENGE_DIR="$REPO_ROOT/adventures/planned/00-blind-by-design/intermediate"

# Make the Maven wrapper executable so the participant can just `./mvnw ...`
if [[ -f "$CHALLENGE_DIR/mvnw" ]]; then
chmod +x "$CHALLENGE_DIR/mvnw"
fi

echo "✨ Pre-warming the Maven dependency cache so the first ./mvnw is fast..."
( cd "$CHALLENGE_DIR" && ./mvnw -q -DskipTests dependency:go-offline ) || \
echo "⚠️ Dependency pre-warm skipped (network or wrapper not ready yet)"

echo "βœ… Post-create complete."
54 changes: 54 additions & 0 deletions .devcontainer/00-blind-by-design_02-intermediate/post-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -e

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
CHALLENGE_DIR="$REPO_ROOT/adventures/planned/00-blind-by-design/intermediate"

cat <<EOF

✨ Level 2 - 🟑 Intermediate (Outcome by cohort)

πŸ“‚ Challenge directory:
$CHALLENGE_DIR

πŸ§ͺ Sibling services already running (managed by devcontainer compose):
- flagd β†’ reachable at flagd:8013 (gRPC eval) / flagd:8016 (OFREP HTTP)
Management/metrics on :8014, sync stream on :8015.
All four ports are forwarded to localhost.

β–Ά Run the lab β€” three named launch configs ship in .vscode/launch.json:
πŸ‡©πŸ‡ͺ Run the Lab β€” Germany (COUNTRY=de)
πŸ‡¦πŸ‡Ή Run the Lab β€” Austria (COUNTRY=at)
🌍 Run the Lab β€” No country
Open the Run and Debug view (Ctrl/Cmd + Shift + D) and pick one.

Or from the terminal:
./run-germany.sh # COUNTRY=de + tee app.log
./run-austria.sh # COUNTRY=at + tee app.log

πŸ‘‰ In another terminal, exercise the cohorts:
curl 'http://localhost:8080/?species=zyklop' # per-subject targeting
curl 'http://localhost:8080/' # falls through to country branch

βœ… Run the verification when you're ready:
./verify.sh
or use the πŸ§ͺ Verify Solution task: Tasks β†’ Run Test Task.

EOF

# Track that the environment is ready
# shellcheck disable=SC1091
source "$REPO_ROOT/lib/scripts/tracker.sh"
set_tracking_context "blind-by-design" "intermediate"
track_codespace_initialized

# Open the relevant files in the connected editor. customizations.codespaces.openFiles
# is unreliable for dockerComposeFile-based devcontainers (the orchestrator merges
# devcontainer.json and the field is sometimes dropped). `code` is the same CLI the
# editor uses internally and works against either the web or desktop client.
if command -v code >/dev/null 2>&1; then
code "$REPO_ROOT/adventures/planned/00-blind-by-design/docs/intermediate.md" \
"$CHALLENGE_DIR/src/main/java/dev/openfeature/demo/java/demo/OpenFeatureConfig.java" \
"$CHALLENGE_DIR/flags.json" \
2>/dev/null || true
fi
41 changes: 41 additions & 0 deletions .devcontainer/00-blind-by-design_03-expert/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "πŸ§ͺ Adventure 00 | πŸ”΄ Expert (Phase 3 β€” read the chart)",
"dockerComposeFile": "docker-compose.yml",
"service": "workspace",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}/adventures/planned/00-blind-by-design/expert",
"postCreateCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_03-expert/post-create.sh",
"postStartCommand": "bash /workspaces/${localWorkspaceFolderBasename}/.devcontainer/00-blind-by-design_03-expert/post-start.sh",
"customizations": {
"vscode": {
"extensions": [
"vscjava.vscode-java-pack",
"redhat.vscode-yaml",
"ms-azuretools.vscode-docker"
]
},
"codespaces": {
"openFiles": [
"adventures/planned/00-blind-by-design/docs/expert.md",
"adventures/planned/00-blind-by-design/expert/src/main/java/dev/openfeature/demo/java/demo/OpenTelemetryConfig.java",
"adventures/planned/00-blind-by-design/expert/src/main/java/dev/openfeature/demo/java/demo/OpenFeatureConfig.java",
"adventures/planned/00-blind-by-design/expert/flags.json"
]
}
},
"forwardPorts": [8080, 3000, 4317, 4318, 9090, 3200, 8013, 8014, 8015, 8016],
"portsAttributes": {
"8080": { "label": "Spring Boot lab", "onAutoForward": "notify" },
"3000": { "label": "Grafana", "onAutoForward": "notify" },
"4317": { "label": "OTLP gRPC", "onAutoForward": "ignore" },
"4318": { "label": "OTLP HTTP", "onAutoForward": "ignore" },
"9090": { "label": "Prometheus", "onAutoForward": "ignore" },
"3200": { "label": "Tempo HTTP API", "onAutoForward": "ignore" },
"8013": { "label": "flagd gRPC eval", "onAutoForward": "ignore" },
"8014": { "label": "flagd management/metrics", "onAutoForward": "ignore" },
"8015": { "label": "flagd sync (IN_PROCESS)", "onAutoForward": "ignore" },
"8016": { "label": "flagd OFREP", "onAutoForward": "ignore" }
},
"otherPortsAttributes": {
"onAutoForward": "ignore"
}
}
Loading