ai-fob 1.10.1 → 1.11.2

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/README.md CHANGED
@@ -78,12 +78,37 @@ All Claude Code presets (coding + cc-assets). Does NOT include `pi-coding`, `pi-
78
78
  Run `npx ai-fob@latest --preset coding` to upgrade.
79
79
 
80
80
  - **Unmodified files** are updated to the latest version
81
- - **Locally modified customizable files** (like `testing-and-validation/SKILL.md`) are preserved -- the new version is saved as `.ai-fob-new` for you to compare
81
+ - **Locally modified customizable files** (like `statusline.sh`) are preserved -- the new version is saved as `.ai-fob-new` for you to compare
82
82
  - **Your custom assets** (agents, skills, commands you created) are never touched
83
83
  - **settings.json** is deep-merged -- your existing settings are preserved, new keys are added
84
+ - **Per-project runtime configuration** lives in `runtime/` at the project root (not inside `.claude/` or `.pi/`) and is never overwritten by ai-fob. Populate `runtime/project.json` via `/setup-project`; copy `runtime/auth.example.json` to `runtime/auth.json` and fill local test credentials when needed (the latter is gitignored).
84
85
 
85
86
  A `.ai-fob.json` file in your `.claude/` directory tracks what was installed and detects modifications.
86
87
 
88
+ ## Upgrading from v1.10.x
89
+
90
+ v1.11 moves per-project credentials (`auth.json`) and adds a per-project `project.json` for scripts/URL/mobile config. Both live in a shared `runtime/` directory at the project root, replacing the v1.10 per-skill `.claude/skills/testing-and-validation/auth.json` (and Pi equivalent).
91
+
92
+ Two-step upgrade (the migration command ships *in* v1.11, so the package upgrade comes first):
93
+
94
+ ```bash
95
+ # 1. Install v1.11 — this also installs the /migrate-runtime-config command.
96
+ # The upgrade overwrites your v1.10 SKILL.md files (the customizable
97
+ # carve-out is gone), which is intentional: per-project values now live
98
+ # in runtime/project.json instead.
99
+ npx ai-fob@latest --preset coding
100
+
101
+ # 2. In Claude Code, from your project root, run the one-shot migration:
102
+ /migrate-runtime-config
103
+ ```
104
+
105
+ The migration command:
106
+ - Atomically moves `.claude/skills/testing-and-validation/auth.json` (and Pi equivalent) to `runtime/auth.json`.
107
+ - Recovers your previous v1.10 SKILL.md tables to populate `runtime/project.json`. Because step 1 already overwrote the on-disk SKILL.md, the parser walks `git history` for the pre-upgrade version: it tries `git show HEAD:<path>` first, then walks back through the last 20 commits touching that file.
108
+ - Falls back to a template-only `runtime/project.json` (all values `null`) only when no git history is available or no commit of the v1.10 SKILL.md exists. In that case, populate `runtime/project.json` manually from your previous scripts.
109
+ - Deletes the orphan v1.10 files from both skill directories.
110
+ - Prints a verification summary showing which source it parsed from.
111
+
87
112
  ## License
88
113
 
89
114
  MIT
@@ -38,7 +38,8 @@ For BLOCKED checks and aggregate-result derivation, follow the **Validation Taxo
38
38
  - Record `criticality`, `executor`, `risk` from the taxonomy.
39
39
  - Apply the **overall-result derivation** rules to compute the aggregate result: `pass | fail | blocked | pass-with-followups`.
40
40
  - Apply the **self-consistency requirement**: if no terminal blockers exist, the overall result must be `pass-with-followups`, not `blocked`.
41
- - For auth-required browser checks, follow the **Auth and Provider Runtime Configuration** section of the same skill (look up `.claude/skills/testing-and-validation/auth.json`; never mark auth BLOCKED if valid credentials are configured; never print raw secrets).
41
+ - For auth-required browser checks, follow the **Project Runtime Configuration** section of the same skill (look up `runtime/auth.json` at the project root; never mark auth BLOCKED if valid credentials are configured; never print raw secrets).
42
+ - For project-specific scripts, testing URL, and mobile devices, read from `runtime/project.json` at the project root.
42
43
 
43
44
  The detailed check schema (Criticality/Executor/Risk columns, `[HL]` prefix semantics, fix-cycle interaction) is provided by the calling command's spawn prompt — this agent applies the taxonomy generically and lets the spawn prompt specify column layout.
44
45
 
@@ -48,7 +49,7 @@ The calling prompt provides a numbered list of checks to run. Execute every chec
48
49
 
49
50
  1. Read the check name and description from the prompt
50
51
  2. Determine the check type from the description and execute accordingly:
51
- - **Shell command checks**: Run the command via Bash. Record exit code and output. PASS if exit code is 0 and output matches expectations; FAIL otherwise. Include the first 50 lines of output on failure. Use the exact scripts from the testing-and-validation skill -- do not guess or improvise commands. If a script does not exist, FAIL the check with "Script not found: {path}".
52
+ - **Shell command checks**: Run the command via Bash. Record exit code and output. PASS if exit code is 0 and output matches expectations; FAIL otherwise. Include the first 50 lines of output on failure. Use the exact scripts from `runtime/project.json` (read `scripts.*` paths) -- do not guess or improvise commands. If a configured script path does not exist, FAIL the check with "Script not found: {path}".
52
53
  - **File verification checks**: Use Read/Grep/Glob to verify file existence, content patterns, and structural requirements. PASS if all conditions met; FAIL otherwise.
53
54
  - **Browser verification checks**: Use the agent-browser skill workflow -- navigate (`agent-browser open <url>`), snapshot (`agent-browser snapshot -i`), interact, re-snapshot. Use `agent-browser screenshot` to capture visual state as evidence. Always check the browser console for JavaScript errors as part of browser checks. Compare actual page state against expected outcomes described in the check. Close the browser session when all browser checks are complete (`agent-browser close`).
54
55
  3. Record specific findings (command output, file paths verified, screenshots taken, elements observed)
@@ -989,19 +989,27 @@ Store the total check count as `BUILD_CHECK_COUNT`. Store the count of HL criter
989
989
 
990
990
  Determine if browser checks exist: scan the assembled check list for any check that mentions "browser", "agent-browser", "navigate", "page", "UI", or "localhost". Store as `HAS_BROWSER_CHECKS` (true/false).
991
991
 
992
- If `HAS_BROWSER_CHECKS` is true: Read the Mobile Test Devices section from the testing-and-validation skill. Extract the Primary Device and Secondary Device values. Store as `MOBILE_PRIMARY_DEVICE` and `MOBILE_SECONDARY_DEVICE`. Determine `HAS_MOBILE_CHECKS`:
993
- - If `MOBILE_PRIMARY_DEVICE` is "NONE": set `HAS_MOBILE_CHECKS = false`.
992
+ If `HAS_BROWSER_CHECKS` is true: Read `runtime/project.json`. Extract `mobile.primaryDevice` and `mobile.secondaryDevice`. Store as `MOBILE_PRIMARY_DEVICE` and `MOBILE_SECONDARY_DEVICE`. Determine `HAS_MOBILE_CHECKS`:
993
+ - If `MOBILE_PRIMARY_DEVICE` is `null` (or missing): set `HAS_MOBILE_CHECKS = false`.
994
994
  - Otherwise: set `HAS_MOBILE_CHECKS = true`.
995
995
 
996
996
  If `HAS_BROWSER_CHECKS` is false: set `HAS_MOBILE_CHECKS = false`.
997
997
 
998
+ Also read from `runtime/project.json`:
999
+ - `testing.testingUrl` → store as `TESTING_URL` (used for browser checks and dev-server readiness probes).
1000
+ - `scripts.devAll` → store as `DEV_SERVER_SCRIPT` (used to start the dev server).
1001
+
1002
+ If `runtime/project.json` is missing, OR `testing.testingUrl` is null/missing, OR `scripts.devAll` is null/missing when `HAS_BROWSER_CHECKS` is true: warn "runtime/project.json is missing or incomplete. Browser checks will be marked BLOCKED with terminal severity." Set `HAS_BROWSER_CHECKS = false` and skip 5b (dev server cannot start without a configured script).
1003
+
1004
+ Derive `TESTING_URL_PORT` by parsing the port from `TESTING_URL` (default 3000 if no explicit port — e.g. `http://localhost` → 80, but localhost without port is treated as 3000 for back-compat unless an explicit port is present).
1005
+
998
1006
  #### 5b. Start Dev Server (if needed)
999
1007
 
1000
1008
  If `HAS_BROWSER_CHECKS` is true:
1001
1009
 
1002
- 1. Kill any existing process on port 3000: run `lsof -ti:3000 | xargs kill -9 2>/dev/null` via Bash. Ignore errors (no process on port is fine).
1003
- 2. Start the dev server via Bash with `run_in_background: true`: `./scripts/dev.sh > /tmp/dev-server.log 2>&1 &` (from the testing-and-validation skill). The trailing `&` is CRITICAL — it ensures the shell backgrounds the process so the Bash call returns immediately regardless of the `run_in_background` parameter.
1004
- 3. Wait for the server to be ready: poll `curl -s -o /dev/null -w "%{http_code}" http://localhost:3000` via Bash every 3 seconds, up to 60 seconds. A response of 200 or 3xx means ready.
1010
+ 1. Kill any existing process on `TESTING_URL_PORT`: run `lsof -ti:{TESTING_URL_PORT} | xargs kill -9 2>/dev/null` via Bash. Ignore errors (no process on port is fine).
1011
+ 2. Start the dev server via Bash with `run_in_background: true`: `{DEV_SERVER_SCRIPT} > /tmp/dev-server.log 2>&1 &` (from `runtime/project.json` `scripts.devAll`). The trailing `&` is CRITICAL — it ensures the shell backgrounds the process so the Bash call returns immediately regardless of the `run_in_background` parameter.
1012
+ 3. Wait for the server to be ready: poll `curl -s -o /dev/null -w "%{http_code}" {TESTING_URL}` via Bash every 3 seconds, up to 60 seconds. A response of 200 or 3xx means ready.
1005
1013
  4. If no successful response within 60 seconds: warn "Dev server did not start within 60 seconds. Browser checks may fail." Proceed anyway -- the validator will FAIL browser checks if the server is unreachable.
1006
1014
  5. Store `DEV_SERVER_STARTED = true`.
1007
1015
 
@@ -1031,26 +1039,25 @@ Read the build report(s) for context on what was built:
1031
1039
 
1032
1040
  ## Dev Server
1033
1041
  {If DEV_SERVER_STARTED is true:
1034
- "A dev server is already running at http://localhost:3000 (from the testing-and-validation skill Testing URL). Use this URL for all browser-based checks. Do NOT start your own dev server."
1042
+ "A dev server is already running at {TESTING_URL} (from runtime/project.json `testing.testingUrl`). Use this URL for all browser-based checks. Do NOT start your own dev server."
1035
1043
  If DEV_SERVER_STARTED is false:
1036
1044
  "No dev server is running. There are no browser checks in this validation."}
1037
1045
 
1038
1046
  ## Test Credentials
1039
- {Look up `.claude/skills/testing-and-validation/auth.json` (the gitignored local credentials file). If `auth.json` does not exist, fall back to the legacy SKILL.md fields ("Login URL / Username / Password / Post-Login URL" in the Test Credentials table) for backward compatibility with projects scaffolded before auth.json was introduced.
1040
-
1041
- Source resolution:
1042
- PRIMARY: `.claude/skills/testing-and-validation/auth.json` if it exists. Read `browserAuth.enabled`, `browserAuth.loginUrl`, `browserAuth.postLoginUrl`, `browserAuth.users.primary.username`, `browserAuth.users.primary.password`.
1043
- FALLBACK: the Test Credentials section of `.claude/skills/testing-and-validation/SKILL.md` (Login URL / Username / Password / Post-Login URL).
1047
+ {Look up `runtime/auth.json` (the gitignored, project-root credentials file). Read `browserAuth.enabled`, `browserAuth.loginUrl`, `browserAuth.postLoginUrl`, `browserAuth.users.primary.username`, `browserAuth.users.primary.password`.
1044
1048
 
1045
1049
  Then branch:
1046
1050
 
1047
- If browserAuth.enabled is true AND primary username is set AND is NOT "REPLACE_WITH_TEST_USERNAME" (or legacy Username is NOT "NONE" and NOT "REPLACE_WITH_TEST_USERNAME"):
1051
+ If `runtime/auth.json` does NOT exist:
1052
+ "Test credentials are NOT available. Browser checks that require authentication MUST be marked as BLOCKED with severity classification per the Validation Taxonomy in the testing-and-validation skill. Use reason: 'runtime/auth.json not present — user must copy runtime/auth.example.json to runtime/auth.json and populate.' Mark severity as non-terminal if substitute evidence exists; mark terminal if the entire phase depends on authenticated browser flow."
1053
+
1054
+ Else if browserAuth.enabled is true AND primary username is set AND is NOT "REPLACE_WITH_TEST_USERNAME":
1048
1055
  "Test credentials are configured. You MUST authenticate before running browser checks that require a logged-in session.
1049
1056
 
1050
- - Login URL: {browserAuth.loginUrl OR legacy Login URL}
1057
+ - Login URL: {browserAuth.loginUrl}
1051
1058
  - Username: [REDACTED — present]
1052
1059
  - Password: [REDACTED — present]
1053
- - Post-Login URL: {browserAuth.postLoginUrl OR legacy Post-Login URL}
1060
+ - Post-Login URL: {browserAuth.postLoginUrl}
1054
1061
 
1055
1062
  Authentication procedure (use the agent-browser skill workflow):
1056
1063
  1. Open the Login URL: `agent-browser open {Login URL}`
@@ -1060,23 +1067,21 @@ Read the build report(s) for context on what was built:
1060
1067
  5. Click the submit/login button
1061
1068
  6. Wait for navigation to the Post-Login URL: `agent-browser wait --url \"**{Post-Login URL path}\"`
1062
1069
  7. Take a snapshot to confirm successful login
1063
- 8. Save the authenticated browser state: `agent-browser state save browser-state.json` (NOTE: this is the agent-browser playwright state file; it is DISTINCT from the credentials file `auth.json` in the skill directory)
1070
+ 8. Save the authenticated browser state: `agent-browser state save browser-state.json` (NOTE: this is the agent-browser playwright state file; it is DISTINCT from the credentials file `runtime/auth.json`)
1064
1071
  9. For subsequent browser checks, load the saved state: `agent-browser state load browser-state.json`
1065
1072
 
1066
1073
  REDACTION REQUIREMENT: Never print raw usernames, passwords, API keys, webhook secrets, or full credential values in your report, evidence, or summary. Redact as `[REDACTED]` or report only presence/absence. Failure to redact must be treated as a critical reporting bug.
1067
1074
 
1068
1075
  IMPORTANT: Because credentials are configured, you MUST NOT mark any browser check as BLOCKED due to authentication requirements. If authentication fails, mark the check as FAIL (not BLOCKED) and include the error details with credentials redacted."
1069
1076
 
1070
- If browserAuth.enabled is false AND no legacy credentials configured (or Username is "NONE"):
1077
+ Else if browserAuth.enabled is false:
1071
1078
  "This project does not require authentication for browser tests. No login step is needed."
1072
1079
 
1073
- If neither auth.json nor legacy SKILL.md contain valid credentials (auth.json missing AND legacy Username is "REPLACE_WITH_TEST_USERNAME", or auth.json browserAuth.enabled is true but users.primary.username is "REPLACE_WITH_TEST_USERNAME"):
1074
- "Test credentials have NOT been configured. Browser checks that require authentication MUST be marked as BLOCKED with severity classification per the Validation Taxonomy in the testing-and-validation skill. Use reason: 'Test credentials not configured (no auth.json present and SKILL.md placeholder unchanged) — user must populate .claude/skills/testing-and-validation/auth.json from auth.example.json.' Mark severity as non-terminal if substitute evidence for the underlying behavior exists (source review, build/lint/typecheck pass, adjacent non-auth flows pass); mark terminal if the entire phase depends on authenticated browser flow."}
1080
+ Else (auth.json exists, browserAuth.enabled is true, but primary username is still the placeholder):
1081
+ "Test credentials have NOT been populated. runtime/auth.json exists with `browserAuth.enabled: true` but `users.primary.username` is still `REPLACE_WITH_TEST_USERNAME`. Browser checks that require authentication MUST be marked as BLOCKED with severity classification per the Validation Taxonomy. Use reason: 'runtime/auth.json present but credentials are placeholders — user must populate users.primary.username/password.' Mark severity as non-terminal if substitute evidence exists; mark terminal if the entire phase depends on authenticated browser flow."}
1075
1082
 
1076
1083
  ## Mobile Device Testing
1077
- {Read the Mobile Test Devices section from the testing-and-validation skill. Extract the Primary Device and Secondary Device values.
1078
-
1079
- If HAS_MOBILE_CHECKS is true (Primary Device is NOT "NONE"):
1084
+ {If HAS_MOBILE_CHECKS is true (`mobile.primaryDevice` in runtime/project.json is NOT null):
1080
1085
  "Mobile viewport testing is configured for this project. After completing each browser check at the default desktop viewport, you MUST repeat the visual/layout portions of that check at the mobile viewport.
1081
1086
 
1082
1087
  Mobile testing procedure (use the agent-browser skill):
@@ -1086,13 +1091,13 @@ Read the build report(s) for context on what was built:
1086
1091
  4. Take a snapshot to verify layout at mobile viewport: `agent-browser snapshot -i`
1087
1092
  5. Take a screenshot for visual evidence: `agent-browser screenshot`
1088
1093
  6. Verify the page renders correctly at the mobile viewport -- no overlapping elements, no horizontal scrolling, no truncated content, no inaccessible interactive elements
1089
- 7. Reset to desktop viewport when done: `agent-browser set viewport 1920 1080`{If MOBILE_SECONDARY_DEVICE is not "NONE":
1094
+ 7. Reset to desktop viewport when done: `agent-browser set viewport 1920 1080`{If MOBILE_SECONDARY_DEVICE is not null:
1090
1095
  8. Repeat steps 2-7 with the secondary device: `agent-browser set device \"{MOBILE_SECONDARY_DEVICE}\"`}
1091
1096
 
1092
1097
  For each browser check, report desktop and mobile results separately. If a check passes at desktop but fails at mobile, the overall check result is FAIL. Include the device name in the findings (e.g., 'FAIL at iPhone 12 Pro: navigation menu overlaps content')."
1093
1098
 
1094
1099
  If HAS_MOBILE_CHECKS is false:
1095
- "Mobile viewport testing is not configured (Primary Device is NONE in the testing-and-validation skill). All browser checks run at the default desktop viewport only."}
1100
+ "Mobile viewport testing is not configured (`mobile.primaryDevice` is null in runtime/project.json). All browser checks run at the default desktop viewport only."}
1096
1101
 
1097
1102
  ## Validation Checks ({BUILD_CHECK_COUNT} -- run ALL of these)
1098
1103
 
@@ -1164,7 +1169,7 @@ Then branch on `result`:
1164
1169
  - **If `result: pass-with-followups`**: Set `BUILD_VALIDATION_RESULT = "pass-with-followups"`. Extract the same fields. The build is accepted; non-terminal follow-ups will be surfaced in the phase completion report for human action. Proceed to 5f (Cleanup). Do NOT enter the fix loop — non-terminal blockers are operator/dashboard/external work, not code defects.
1165
1170
  - **If `result: fail`**: Extract all five fields. Proceed to 5e (Fix and Re-validate).
1166
1171
  - **If `result: blocked`**: Extract all five fields. This means at least one TERMINAL blocker exists. Auth-credentials recovery check (constrained):
1167
- - Review each terminal BLOCKED check reason. If a BLOCKED check cites "authentication", "login", "credentials", or "authenticated session" AND `auth.json` is present with `browserAuth.enabled: true` AND `users.primary.username` is set (or legacy SKILL.md Username is not "NONE" and not "REPLACE_WITH_TEST_USERNAME") AND this is the FIRST cycle (BUILD_VALIDATION_CYCLE == 1) AND the check has no documented substitute evidence: re-spawn the validator ONCE without incrementing BUILD_VALIDATION_CYCLE. Log: "Validator marked auth-requiring check as BLOCKED despite configured credentials on first cycle. Re-running once without cycle increment."
1172
+ - Review each terminal BLOCKED check reason. If a BLOCKED check cites "authentication", "login", "credentials", or "authenticated session" AND `runtime/auth.json` is present with `browserAuth.enabled: true` AND `users.primary.username` is set and is not `"REPLACE_WITH_TEST_USERNAME"` AND this is the FIRST cycle (BUILD_VALIDATION_CYCLE == 1) AND the check has no documented substitute evidence: re-spawn the validator ONCE without incrementing BUILD_VALIDATION_CYCLE. Log: "Validator marked auth-requiring check as BLOCKED despite configured credentials on first cycle. Re-running once without cycle increment."
1168
1173
  - The no-increment recovery is allowed ONLY when ALL of the conditions above hold. On any subsequent cycle, on any non-auth-related blocker, or when credentials are unconfigured, increment the cycle normally and treat the result as terminal-blocked.
1169
1174
  - If recovery does not apply, set `BUILD_VALIDATION_RESULT = "blocked"`. Do NOT enter the fix loop — genuinely BLOCKED checks cannot be fixed by a builder agent. Proceed to 5f (Cleanup). The terminal blocked checks will be surfaced in the phase completion report for human action.
1170
1175
 
@@ -1257,7 +1262,7 @@ If GIT_AVAILABLE == true:
1257
1262
  ##### 5e-iii. Dev server check
1258
1263
 
1259
1264
  If DEV_SERVER_STARTED == true:
1260
- 1. Check if the dev server is still responding: run `curl -s -o /dev/null -w "%{http_code}" http://localhost:3000` via Bash.
1265
+ 1. Check if the dev server is still responding: run `curl -s -o /dev/null -w "%{http_code}" {TESTING_URL}` via Bash.
1261
1266
  2. If NOT responding (no 200/3xx): restart the dev server using the same approach as 5b (kill port, start background, poll). Update any process tracking.
1262
1267
  3. If responding: no action needed. Hot reload should pick up code changes for most frameworks.
1263
1268
 
@@ -0,0 +1,248 @@
1
+ ---
2
+ description: One-shot migration from the v1.10 skill-local auth.json layout to the v1.11 root-level runtime/ layout. Moves credentials, parses old SKILL.md tables into runtime/project.json, deletes orphan files.
3
+ ---
4
+
5
+ # Migrate Runtime Config (v1.10 → v1.11)
6
+
7
+ This command is for projects that were set up under ai-fob **v1.10.x**, which placed `auth.json` and `auth.example.json` inside each skill directory (`.claude/skills/testing-and-validation/` and `.pi/skills/testing-and-validation/`). v1.11.0 consolidates that configuration into a shared `runtime/` directory at the project root, plus a new `runtime/project.json` for scripts/testing/mobile.
8
+
9
+ Run this command ONCE **after** running `npx ai-fob@latest --preset coding` (the upgrade is what installs this command in the first place). The upgrade overwrites your v1.10 `SKILL.md` files before you can invoke the migration, so this command walks `git history` to recover the pre-upgrade SKILL.md and parse its tables. It is idempotent — running a second time detects the already-migrated state and exits cleanly.
10
+
11
+ ## When to use
12
+
13
+ - You just upgraded to v1.11 (the new SKILL.md was installed) AND
14
+ - You have `.claude/skills/testing-and-validation/auth.json` or `.pi/skills/testing-and-validation/auth.json` on disk OR your git history contains a v1.10-shape SKILL.md, AND
15
+ - You do not yet have a `runtime/` directory at the project root.
16
+
17
+ If neither of the v1.10 files exist (e.g. fresh project), skip this command — run `/setup-project` for a fresh scaffold instead.
18
+
19
+ ## Workflow
20
+
21
+ ### Step 0: Pre-flight
22
+
23
+ 1. Determine `PROJECT_ROOT` (current working directory).
24
+ 2. Check whether `runtime/` already exists at the project root.
25
+ - If `runtime/project.json` exists AND `runtime/auth.example.json` exists: print `Already migrated. runtime/ exists at <PROJECT_ROOT>/runtime/. Exiting.` and STOP.
26
+ - If `runtime/` exists but is partial (e.g. only `auth.json`): print a warning, list what's there, and ask the user whether to continue (which will fill in the missing template/.gitignore files but not overwrite existing files).
27
+ 3. Check whether any v1.10 source exists:
28
+ - `CLAUDE_AUTH_JSON` = `.claude/skills/testing-and-validation/auth.json` (may or may not exist)
29
+ - `CLAUDE_AUTH_EXAMPLE` = `.claude/skills/testing-and-validation/auth.example.json`
30
+ - `CLAUDE_GITIGNORE` = `.claude/skills/testing-and-validation/.gitignore`
31
+ - `PI_AUTH_JSON` = `.pi/skills/testing-and-validation/auth.json`
32
+ - `PI_AUTH_EXAMPLE` = `.pi/skills/testing-and-validation/auth.example.json`
33
+ - `PI_GITIGNORE` = `.pi/skills/testing-and-validation/.gitignore`
34
+ - `CLAUDE_SKILL_MD` = `.claude/skills/testing-and-validation/SKILL.md`
35
+ - `PI_SKILL_MD` = `.pi/skills/testing-and-validation/SKILL.md`
36
+ 4. If neither `CLAUDE_AUTH_JSON` nor `PI_AUTH_JSON` exists AND no v1.10-shaped SKILL.md tables are present, print: `No v1.10 runtime configuration detected. Nothing to migrate. Run /setup-project for a fresh scaffold.` and STOP.
37
+ 5. Initialize a `MIGRATION_LOG` array — every action taken gets appended for the final report.
38
+
39
+ ### Step 1: Create runtime/ with templates and .gitignore
40
+
41
+ 1. Create the directory `runtime/` (if it does not already exist).
42
+
43
+ 2. Write `runtime/auth.example.json` (only if it does not already exist) with this content verbatim:
44
+ ```json
45
+ {
46
+ "$schemaComment": "Copy this file to runtime/auth.json and fill local/dev-only values. Never commit runtime/auth.json.",
47
+ "browserAuth": {
48
+ "enabled": false,
49
+ "loginUrl": "http://localhost:3000/login",
50
+ "postLoginUrl": "http://localhost:3000/dashboard",
51
+ "users": {
52
+ "primary": { "username": "REPLACE_WITH_TEST_USERNAME", "password": "REPLACE_WITH_TEST_PASSWORD" },
53
+ "secondary": { "username": "REPLACE_WITH_SECONDARY_TEST_USERNAME", "password": "REPLACE_WITH_SECONDARY_TEST_PASSWORD" }
54
+ }
55
+ },
56
+ "providers": {
57
+ "clerk": { "enabled": false, "publishableKey": "", "secretKey": "", "webhookSecret": "", "dashboardUrl": "https://dashboard.clerk.com/", "allowReadOnlyApiAccess": false, "allowMutations": false },
58
+ "convex": { "enabled": false, "deploymentUrl": "", "deployKey": "", "dashboardUrl": "https://dashboard.convex.dev/", "allowReadOnlyApiAccess": false, "allowMutations": false }
59
+ },
60
+ "safety": {
61
+ "allowExternalAccountCreation": false,
62
+ "allowDestructiveProviderActions": false,
63
+ "allowSecretMutation": false,
64
+ "allowProductionLikeTargets": false
65
+ },
66
+ "notes": {
67
+ "redaction": "Agents must never print passwords, tokens, API keys, webhook secrets, or full credential values in reports/logs.",
68
+ "scope": "Use only local/dev/test credentials. Do not place production secrets here."
69
+ }
70
+ }
71
+ ```
72
+
73
+ 3. Write `runtime/project.example.json` (only if it does not already exist) with this content verbatim:
74
+ ```json
75
+ {
76
+ "$schemaComment": "Per-project scripts and testing configuration. Committed to git. Populate via /setup-project. Update manually as scripts evolve. null means 'not configured for this project'.",
77
+ "scripts": {
78
+ "devAll": "./scripts/dev.sh",
79
+ "devFrontend": "./scripts/dev-frontend.sh",
80
+ "devBackend": "./scripts/dev-backend.sh",
81
+ "lint": "./scripts/lint.sh",
82
+ "typecheck": "./scripts/typecheck.sh",
83
+ "format": "./scripts/format.sh",
84
+ "build": "./scripts/build.sh",
85
+ "dockerBuild": "./scripts/docker-build.sh",
86
+ "dockerRun": "./scripts/docker-run.sh",
87
+ "test": null,
88
+ "testE2E": null
89
+ },
90
+ "testing": {
91
+ "testingUrl": "http://localhost:3000",
92
+ "backendUrl": null,
93
+ "testRunner": null
94
+ },
95
+ "mobile": {
96
+ "primaryDevice": "iPhone 12 Pro",
97
+ "secondaryDevice": null
98
+ }
99
+ }
100
+ ```
101
+
102
+ 4. Write `runtime/.gitignore` (only if it does not already exist) with content verbatim:
103
+ ```
104
+ # Local-only runtime credentials. Never commit.
105
+ auth.json
106
+ ```
107
+
108
+ Log each file you created.
109
+
110
+ ### Step 2: Migrate auth.json (the user's actual credentials)
111
+
112
+ 1. If both `CLAUDE_AUTH_JSON` and `PI_AUTH_JSON` exist:
113
+ - Compare their contents with `diff`.
114
+ - If identical, prefer `CLAUDE_AUTH_JSON` as the source.
115
+ - If different, print a warning: `Conflict: .claude/.../auth.json and .pi/.../auth.json differ. Using Claude version; Pi version will be deleted. Review the diff below if you need to merge manually before continuing.` Print the diff. Ask the user whether to continue (yes/no).
116
+ 2. Move whichever auth.json exists to `runtime/auth.json` using `mv`. Do NOT use `cp` then delete — atomic move preserves file permissions and avoids leaving a window where credentials are duplicated on disk.
117
+ 3. If `runtime/auth.json` already exists (unlikely given Step 0), do not overwrite. Print a warning and leave both files in place for the user to reconcile.
118
+
119
+ Log the move.
120
+
121
+ ### Step 3: Build runtime/project.json from the old SKILL.md tables
122
+
123
+ The pre-v1.11 SKILL.md carried three populated tables: Project Scripts, Testing Configuration, Mobile Test Devices. Parse them from whichever variant still has them.
124
+
125
+ 1. Resolve the source SKILL.md to parse. Define the test "has v1.10 tables" as: file contents contain both the header line `## Project Scripts` AND the header line `## Mobile Test Devices`. The v1.11 SKILL.md contains NEITHER header, so this reliably distinguishes the two shapes.
126
+
127
+ Walk this priority chain, stop at the FIRST source that passes the "has v1.10 tables" test. Record which source won as `PARSE_SOURCE_LABEL`:
128
+
129
+ a. `CLAUDE_SKILL_MD` on disk (label: `.claude/skills/testing-and-validation/SKILL.md (on disk, pre-upgrade)`).
130
+ b. `PI_SKILL_MD` on disk (label: `.pi/skills/testing-and-validation/SKILL.md (on disk, pre-upgrade)`).
131
+ c. If `.git/` exists at the project root: `git show HEAD:.claude/skills/testing-and-validation/SKILL.md` (label: `git HEAD .claude/skills/testing-and-validation/SKILL.md`). Capture stderr — if the file did not exist at HEAD, this fails with non-zero exit, treat as "not found" and continue.
132
+ d. `git show HEAD:.pi/skills/testing-and-validation/SKILL.md` (label: `git HEAD .pi/skills/testing-and-validation/SKILL.md`).
133
+ e. Walk back through git history for the Claude variant: run `git log --format=%H -n 20 -- .claude/skills/testing-and-validation/SKILL.md`. For each commit hash from newest to oldest, run `git show <hash>:.claude/skills/testing-and-validation/SKILL.md` and apply the test. First hit wins (label: `git <short-hash> .claude/skills/testing-and-validation/SKILL.md`).
134
+ f. Same as (e) for the Pi variant.
135
+ g. None of the above produced a v1.10-shape source. Set `PARSE_SOURCE = null` and `PARSE_SOURCE_LABEL = "template-only (no v1.10 SKILL.md found in working tree or git history)"`. Skip to sub-step 7.
136
+
137
+ The 20-commit cap on history walking prevents runaway searches; a project that hasn't touched SKILL.md in 20 commits is fine with the template-only fallback.
138
+
139
+ 2. Read the resolved source content (either from disk in cases a/b, or captured stdout from `git show` in cases c-f).
140
+ 3. Look for the `## Project Scripts` section heading. If found, parse the markdown table rows. For each row, the script category is column 1, the command is column 2. Map:
141
+ - `Run All Dev Servers` → `scripts.devAll`
142
+ - `Run Frontend` → `scripts.devFrontend`
143
+ - `Run Backend` → `scripts.devBackend`
144
+ - `Lint` → `scripts.lint`
145
+ - `Type Check` → `scripts.typecheck`
146
+ - `Format` → `scripts.format`
147
+ - `Build` → `scripts.build`
148
+ - `Docker Build` → `scripts.dockerBuild`
149
+ - `Docker Run` → `scripts.dockerRun`
150
+ - Any row whose command is `"N/A"`, `"NONE"`, empty, or a backtick-wrapped equivalent → set to `null`.
151
+ 4. Look for the `## Testing Configuration` section heading. Parse:
152
+ - `Testing URL` → `testing.testingUrl` (strip backticks)
153
+ - `Backend URL` → `testing.backendUrl`
154
+ - `Test Runner` → `testing.testRunner`
155
+ - `Test Command` → `scripts.test`
156
+ - `E2E Test Command` → `scripts.testE2E`
157
+ - Any value matching `Not configured` / `NONE` / empty → `null`.
158
+ 5. Look for the `## Mobile Test Devices` section heading. Parse:
159
+ - `Primary Device` → `mobile.primaryDevice` (strip backticks)
160
+ - `Secondary Device` → `mobile.secondaryDevice`
161
+ - Any value matching `NONE` (case-insensitive) → `null`.
162
+
163
+ 6. If parsing succeeded (at least the Project Scripts section was found and parsed): write the populated values to `runtime/project.json`, starting from the template in Step 1.3 and overwriting only the fields you successfully parsed.
164
+
165
+ 7. If parsing FAILED OR `PARSE_SOURCE = null`: copy `runtime/project.example.json` to `runtime/project.json` unchanged. Log: `Could not recover v1.10 SKILL.md tables from any source (working tree, git HEAD, or last 20 commits touching SKILL.md). runtime/project.json written from template with all values null; populate manually from your project scripts. PARSE_SOURCE_LABEL: <label>.`
166
+
167
+ Log the source file used and which fields were populated.
168
+
169
+ ### Step 4: Delete orphan v1.10 files from both skill directories
170
+
171
+ Delete (if they exist):
172
+ - `CLAUDE_AUTH_JSON` (if not already moved in Step 2)
173
+ - `CLAUDE_AUTH_EXAMPLE`
174
+ - `CLAUDE_GITIGNORE`
175
+ - `PI_AUTH_JSON` (if not already moved in Step 2 — i.e. when Claude's was the chosen source)
176
+ - `PI_AUTH_EXAMPLE`
177
+ - `PI_GITIGNORE`
178
+
179
+ Do NOT delete `CLAUDE_SKILL_MD` or `PI_SKILL_MD` — those are still in use, and the ai-fob upgrade will overwrite them with the v1.11 versions on next install.
180
+
181
+ Log each deletion.
182
+
183
+ ### Step 5: Verify
184
+
185
+ Run these checks and include results in the report:
186
+
187
+ 1. `test -f runtime/project.json` — must exist.
188
+ 2. `test -f runtime/auth.example.json` — must exist.
189
+ 3. `test -f runtime/project.example.json` — must exist.
190
+ 4. `test -f runtime/.gitignore` — must exist; verify `auth.json` appears as a non-comment line.
191
+ 5. `python3 -c "import json; json.load(open('runtime/project.json'))"` (or `node -e "JSON.parse(require('fs').readFileSync('runtime/project.json'))"`) — must parse.
192
+ 6. If `.git` exists: `git check-ignore runtime/auth.json` — must return exit 0 (path would be ignored).
193
+ 7. `test ! -e .claude/skills/testing-and-validation/auth.json` — old file should be gone.
194
+ 8. `test ! -e .pi/skills/testing-and-validation/auth.json` — old file should be gone.
195
+
196
+ If any check fails, mark the migration `INCOMPLETE` and surface the failing check in the report.
197
+
198
+ ### Step 6: Report
199
+
200
+ Present a summary to the user:
201
+
202
+ ```
203
+ RUNTIME CONFIG MIGRATION COMPLETE
204
+
205
+ Project Root: <PROJECT_ROOT>
206
+ Status: COMPLETE | INCOMPLETE
207
+
208
+ Created:
209
+ runtime/auth.example.json (template)
210
+ runtime/project.example.json (template)
211
+ runtime/.gitignore (auth.json ignored)
212
+
213
+ Moved:
214
+ <source path> → runtime/auth.json
215
+
216
+ Populated runtime/project.json from:
217
+ <PARSE_SOURCE_LABEL>
218
+ (resolution chain: working tree → git HEAD → git history walk → template-only fallback)
219
+
220
+ Fields populated: scripts (N), testing (M), mobile (K)
221
+ Fields left null: <list>
222
+
223
+ Deleted (orphan v1.10 files):
224
+ .claude/skills/testing-and-validation/auth.example.json
225
+ .claude/skills/testing-and-validation/.gitignore
226
+ .pi/skills/testing-and-validation/auth.example.json
227
+ .pi/skills/testing-and-validation/.gitignore
228
+ (and any auth.json copies that weren't the migration source)
229
+
230
+ Verification:
231
+ runtime/project.json valid JSON: YES | NO
232
+ runtime/auth.json gitignored: YES | NO | N/A (no git)
233
+
234
+ Next Steps:
235
+ 1. Review runtime/project.json — verify every script path matches a real file under scripts/. Replace any null fields if you have a script for them.
236
+ 2. (Optional) Verify runtime/auth.json contents are still correct.
237
+ 3. Run `npx ai-fob@latest` to upgrade your skill/agent/command/prompt assets to v1.11. The old SKILL.md files will be overwritten (this is intentional — they no longer carry per-project values).
238
+ 4. Commit runtime/project.json, runtime/auth.example.json, runtime/project.example.json, and runtime/.gitignore. Do NOT commit runtime/auth.json.
239
+
240
+ If anything in this report looks wrong, restore from your git history (the v1.10 files were tracked) and report the issue.
241
+ ```
242
+
243
+ ## Edge cases
244
+
245
+ - **No v1.10 auth.json on disk** (only the example file): still run Step 1 to scaffold templates, skip Step 2, run Step 3 if SKILL.md tables exist, Step 4 to delete the example/.gitignore.
246
+ - **User customized auth.example.json**: it gets deleted in Step 4. The user can recover from git history. We do not preserve example-file edits because they are not user data.
247
+ - **Already-modified SKILL.md**: if the user added their own custom sections, those are lost when ai-fob upgrades the skill in v1.11. We can't preserve them automatically. Recommend the user save a copy before upgrading.
248
+ - **Migration interrupted mid-run**: the operations are not transactional. If the command fails between Step 2 and Step 4, the user has a half-migrated state. Re-running the command should be safe — Step 0's idempotency check catches the `runtime/` exists case, and Step 4 deletions are skipped for already-deleted files.