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
10 changes: 8 additions & 2 deletions elixir/Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.PHONY: help all setup deps build fmt fmt-check lint test coverage ci dialyzer e2e
.PHONY: help all setup deps build fmt fmt-check lint test coverage ci dialyzer e2e launch-app github-pr-media

MIX ?= mix

help:
@echo "Targets: setup, deps, fmt, fmt-check, lint, test, coverage, dialyzer, e2e, ci"
@echo "Targets: setup, deps, fmt, fmt-check, lint, test, coverage, dialyzer, e2e, launch-app, github-pr-media, ci"

setup:
$(MIX) setup
Expand Down Expand Up @@ -36,6 +36,12 @@ dialyzer:
e2e:
SYMPHONY_RUN_LIVE_E2E=1 $(MIX) test test/symphony_elixir/live_e2e_test.exs

launch-app:
./scripts/launch-app

github-pr-media:
./scripts/github-pr-media

ci:
$(MAKE) setup
$(MAKE) build
Expand Down
13 changes: 13 additions & 0 deletions elixir/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ mise exec -- mix build
mise exec -- ./bin/symphony ./WORKFLOW.md
```

### Runtime validation helpers (for unattended app tickets)

```bash
# Boot app and verify it responds on localhost
make launch-app

# Bundle runtime artifacts + manifest for PR handoff
make github-pr-media
```

Equivalent script entrypoints are available at `./scripts/launch-app` and
`./scripts/github-pr-media`.

## Configuration

Pass a custom workflow file path to `./bin/symphony` when starting the service:
Expand Down
18 changes: 16 additions & 2 deletions elixir/WORKFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ Use this only when completion is blocked by missing required tools or missing au
- You may make temporary local proof edits to validate assumptions (for example: tweak a local build input for `make`, or hardcode a UI account / response path) when this increases confidence.
- Revert every temporary proof edit before commit/push.
- Document these temporary proof steps and outcomes in the workpad `Validation`/`Notes` sections so reviewers can follow the evidence.
- If app-touching, run `launch-app` validation and capture/upload media via `github-pr-media` before handoff.
- If app-touching, run runtime validation via `make -C elixir launch-app` (or `elixir/scripts/launch-app`) and collect reviewer media via `make -C elixir github-pr-media` (or `elixir/scripts/github-pr-media`) before handoff.
6. Re-check all acceptance criteria and close any gaps.
7. Before every `git push` attempt, run the required validation for your scope and confirm it passes; if it fails, address issues and rerun until green, then commit and push changes.
8. Attach PR URL to the issue (prefer attachment; use the workpad comment only if attachment is unavailable).
Expand Down Expand Up @@ -267,7 +267,7 @@ Use this only when completion is blocked by missing required tools or missing au
- PR feedback sweep is complete and no actionable comments remain.
- PR checks are green, branch is pushed, and PR is linked on the issue.
- Required PR metadata is present (`symphony` label).
- If app-touching, runtime validation/media requirements from `App runtime validation (required)` are complete.
- If app-touching, runtime validation/media requirements from `App runtime validation (required)` are complete (`make -C elixir launch-app` + `make -C elixir github-pr-media`).

## Guardrails

Expand All @@ -289,6 +289,20 @@ Use this only when completion is blocked by missing required tools or missing au
- Keep issue text concise, specific, and reviewer-oriented.
- If blocked and no workpad exists yet, add one blocker comment describing blocker, impact, and next unblock action.

## App runtime validation (required)

Use these repo-local commands whenever the change touches app runtime behavior/UI:

1. Run runtime boot validation:
- `make -C elixir launch-app`
- Equivalent script: `elixir/scripts/launch-app`
2. Package runtime evidence for reviewer handoff:
- `make -C elixir github-pr-media`
- Equivalent script: `elixir/scripts/github-pr-media [artifact-dir]`
3. Attach or reference the generated manifest/archive paths in the workpad + PR handoff.

These commands are designed to work in unattended Codex sessions without depending on globally installed helper binaries.

## Workpad template

Use this exact structure for the persistent workpad comment and keep it updated in place throughout execution:
Expand Down
51 changes: 51 additions & 0 deletions elixir/scripts/github-pr-media
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail

SOURCE_DIR="${1:-artifacts/runtime}"
OUTPUT_DIR="${OUTPUT_DIR:-artifacts/review}"
mkdir -p "$OUTPUT_DIR"

TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
MANIFEST="$OUTPUT_DIR/github-pr-media-${TIMESTAMP}.md"
ARCHIVE="$OUTPUT_DIR/github-pr-media-${TIMESTAMP}.tar.gz"

if [[ ! -d "$SOURCE_DIR" ]]; then
echo "Source directory '$SOURCE_DIR' does not exist." >&2
exit 1
fi

shopt -s nullglob
FILES=("$SOURCE_DIR"/*)
shopt -u nullglob

if [[ ${#FILES[@]} -eq 0 ]]; then
echo "No media/artifact files found under '$SOURCE_DIR'." >&2
exit 1
fi

tar -czf "$ARCHIVE" -C "$SOURCE_DIR" .

{
echo "## Runtime validation artifacts"
echo
echo "Generated: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
printf 'Source dir: `%s`\n' "$SOURCE_DIR"
printf 'Archive: `%s`\n' "$ARCHIVE"
echo
echo "### Files"
for file in "${FILES[@]}"; do
if [[ -f "$file" ]]; then
size=$(wc -c <"$file" | tr -d ' ')
echo "- $(basename "$file") (${size} bytes)"
else
echo "- $(basename "$file") (non-file entry)"
fi
done
echo
echo "### PR handoff"
echo "1. Upload the archive (or selected files) to the PR/comment workflow."
echo "2. Paste this manifest in the PR comment body for reviewer context."
} >"$MANIFEST"

echo "Created archive: $ARCHIVE"
echo "Created manifest: $MANIFEST"
54 changes: 54 additions & 0 deletions elixir/scripts/launch-app
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
set -euo pipefail

PORT="${PORT:-4010}"
STARTUP_TIMEOUT="${STARTUP_TIMEOUT:-45}"
ARTIFACT_DIR="${ARTIFACT_DIR:-artifacts/runtime}"
MIX_CMD="${MIX_CMD:-mix phx.server}"

mkdir -p "$ARTIFACT_DIR"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
LOG_FILE="$ARTIFACT_DIR/launch-app-${TIMESTAMP}.log"
STATUS_FILE="$ARTIFACT_DIR/launch-app-${TIMESTAMP}.txt"

cleanup() {
if [[ -n "${SERVER_PID:-}" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then
kill "$SERVER_PID" >/dev/null 2>&1 || true
wait "$SERVER_PID" 2>/dev/null || true
fi
}
trap cleanup EXIT

echo "Starting app with: $MIX_CMD (PORT=$PORT)"
PORT="$PORT" $MIX_CMD >"$LOG_FILE" 2>&1 &
SERVER_PID=$!

READY=0
for ((i=1; i<=STARTUP_TIMEOUT; i++)); do
if curl -fsS "http://127.0.0.1:${PORT}" >/dev/null 2>&1; then
READY=1
break
fi
sleep 1
done

if [[ "$READY" -eq 1 ]]; then
{
echo "launch-app: PASS"
echo "url: http://127.0.0.1:${PORT}"
echo "server_pid: ${SERVER_PID}"
echo "log: ${LOG_FILE}"
} >"$STATUS_FILE"
echo "App responded successfully."
echo "Status: $STATUS_FILE"
else
{
echo "launch-app: FAIL"
echo "url: http://127.0.0.1:${PORT}"
echo "timeout_seconds: ${STARTUP_TIMEOUT}"
echo "log: ${LOG_FILE}"
} >"$STATUS_FILE"
echo "App did not become ready in ${STARTUP_TIMEOUT}s." >&2
echo "Status: $STATUS_FILE" >&2
exit 1
fi