gaia-framework 1.65.1 → 1.83.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.
Files changed (57) hide show
  1. package/.claude/commands/gaia-create-stakeholder.md +20 -0
  2. package/.claude/commands/gaia-test-gap-analysis.md +17 -0
  3. package/CLAUDE.md +102 -1
  4. package/README.md +2 -2
  5. package/_gaia/_config/global.yaml +5 -1
  6. package/_gaia/_config/lifecycle-sequence.yaml +20 -0
  7. package/_gaia/_config/skill-manifest.csv +2 -0
  8. package/_gaia/_config/workflow-manifest.csv +3 -1
  9. package/_gaia/core/engine/workflow.xml +11 -1
  10. package/_gaia/core/protocols/review-gate-check.xml +29 -1
  11. package/_gaia/core/workflows/party-mode/steps/step-01-agent-loading.md +60 -9
  12. package/_gaia/creative/workflows/problem-solving/checklist.md +64 -14
  13. package/_gaia/creative/workflows/problem-solving/instructions.xml +367 -22
  14. package/_gaia/creative/workflows/problem-solving/workflow.yaml +31 -1
  15. package/_gaia/dev/agents/_base-dev.md +7 -1
  16. package/_gaia/dev/skills/_skill-index.yaml +9 -0
  17. package/_gaia/dev/skills/figma-integration.md +296 -0
  18. package/_gaia/lifecycle/knowledge/brownfield/config-contradiction-scan.md +137 -0
  19. package/_gaia/lifecycle/knowledge/brownfield/dead-code-scan.md +179 -0
  20. package/_gaia/lifecycle/knowledge/brownfield/test-execution-scan.md +209 -0
  21. package/_gaia/lifecycle/skills/document-rulesets.md +91 -6
  22. package/_gaia/lifecycle/templates/brownfield-scan-doc-code-prompt.md +219 -0
  23. package/_gaia/lifecycle/templates/brownfield-scan-hardcoded-prompt.md +169 -0
  24. package/_gaia/lifecycle/templates/brownfield-scan-integration-seam-prompt.md +127 -0
  25. package/_gaia/lifecycle/templates/brownfield-scan-runtime-behavior-prompt.md +141 -0
  26. package/_gaia/lifecycle/templates/brownfield-scan-security-prompt.md +440 -0
  27. package/_gaia/lifecycle/templates/gap-entry-schema.md +282 -0
  28. package/_gaia/lifecycle/templates/infra-prd-template.md +356 -0
  29. package/_gaia/lifecycle/templates/platform-prd-template.md +431 -0
  30. package/_gaia/lifecycle/templates/prd-template.md +70 -0
  31. package/_gaia/lifecycle/templates/story-template.md +22 -1
  32. package/_gaia/lifecycle/workflows/2-planning/create-ux-design/instructions.xml +52 -3
  33. package/_gaia/lifecycle/workflows/4-implementation/add-feature/checklist.md +1 -1
  34. package/_gaia/lifecycle/workflows/4-implementation/add-feature/instructions.xml +2 -3
  35. package/_gaia/lifecycle/workflows/4-implementation/add-stories/checklist.md +5 -0
  36. package/_gaia/lifecycle/workflows/4-implementation/add-stories/instructions.xml +73 -1
  37. package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/checklist.md +25 -0
  38. package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/instructions.xml +79 -0
  39. package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/workflow.yaml +22 -0
  40. package/_gaia/lifecycle/workflows/4-implementation/create-story/instructions.xml +11 -1
  41. package/_gaia/lifecycle/workflows/4-implementation/retrospective/instructions.xml +21 -1
  42. package/_gaia/lifecycle/workflows/4-implementation/retrospective/workflow.yaml +1 -1
  43. package/_gaia/lifecycle/workflows/4-implementation/validate-story/instructions.xml +11 -0
  44. package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/checklist.md +12 -0
  45. package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/instructions.xml +248 -4
  46. package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/workflow.yaml +1 -0
  47. package/_gaia/testing/workflows/test-gap-analysis/checklist.md +8 -0
  48. package/_gaia/testing/workflows/test-gap-analysis/instructions.xml +53 -0
  49. package/_gaia/testing/workflows/test-gap-analysis/workflow.yaml +38 -0
  50. package/bin/gaia-framework.js +44 -8
  51. package/bin/helpers/derive-bump-label.js +41 -0
  52. package/bin/helpers/validate-bump-labels.js +38 -0
  53. package/gaia-install.sh +96 -21
  54. package/package.json +1 -1
  55. package/_gaia/_memory/tier2-results/.gitkeep +0 -0
  56. package/_gaia/_memory/tier2-results/checkpoint-resume-2026-03-24.yaml +0 -6
  57. package/_gaia/_memory/tier2-results/engine-scenarios-2026-03-22.yaml +0 -14
@@ -30,4 +30,5 @@ output:
30
30
  - "{planning_artifacts}/prd.md"
31
31
  - "{planning_artifacts}/architecture.md"
32
32
  - "{planning_artifacts}/epics-and-stories.md"
33
+ - "{planning_artifacts}/brownfield-scan-test-execution.md"
33
34
  - "{planning_artifacts}/brownfield-onboarding.md"
@@ -0,0 +1,8 @@
1
+ # Test Gap Analysis Checklist
2
+ - [ ] Execution mode determined (coverage or verification)
3
+ - [ ] Test plan scanned for test case IDs and story links
4
+ - [ ] Story files scanned for acceptance criteria
5
+ - [ ] Cross-reference completed — gaps identified
6
+ - [ ] Output follows FR-223 schema (summary count, per-story table, coverage %)
7
+ - [ ] Zero-gap case handled with "No coverage gaps detected" message
8
+ - [ ] Workflow completed within NFR-040 performance constraint (< 60 seconds)
@@ -0,0 +1,53 @@
1
+ <workflow name="test-gap-analysis">
2
+ <critical>
3
+ <mandate>Scan test-plan.md and story files to identify acceptance criteria gaps — NFR-040 requires completion in under 60 seconds</mandate>
4
+ <mandate>Output must follow the FR-223 schema: summary count, per-story gap table, coverage percentage</mandate>
5
+ <mandate>When no gaps are detected, output must state "No coverage gaps detected" with a summary count of zero</mandate>
6
+ </critical>
7
+
8
+ <step n="1" title="Determine Mode">
9
+ <action>Check the --mode argument: coverage or verification</action>
10
+ <action>If no mode specified, default to coverage mode</action>
11
+ <action>If mode is 'verification', skip to verification-mode steps (E19-S2 scope — not implemented yet)</action>
12
+ </step>
13
+
14
+ <step n="2" title="Scan Test Plan">
15
+ <action>Read {test_artifacts}/test-plan.md</action>
16
+ <action>Extract all test case IDs and their linked story keys from the test plan</action>
17
+ <action>Build a map of test_case_id -> [story_keys] for cross-referencing</action>
18
+ <action>If test-plan.md is missing, log warning: "test-plan.md not found — partial coverage analysis only" and continue with empty test case map</action>
19
+ </step>
20
+
21
+ <step n="3" title="Scan Story Files">
22
+ <action>Scan all story files in docs/implementation-artifacts/ matching pattern {story_key}-*.md</action>
23
+ <action>For each story file, extract all acceptance criteria (AC items) from the "Acceptance Criteria" section</action>
24
+ <action>Build a map of story_key -> [AC items] with their identifiers (AC1, AC2, etc.)</action>
25
+ <action>Track untested acceptance criteria — ACs that have no matching test case in the test plan map</action>
26
+ </step>
27
+
28
+ <step n="4" title="Cross-Reference and Identify Gaps">
29
+ <action>For each story's acceptance criteria, check if a corresponding test case exists in the test plan</action>
30
+ <action>Flag each AC as covered (has test case) or uncovered-ac (no test case found)</action>
31
+ <action>Calculate coverage rate per story: covered_ACs / total_ACs</action>
32
+ <action>Calculate overall coverage percentage across all stories</action>
33
+ </step>
34
+
35
+ <step n="5" title="Generate Output (FR-223 Schema)">
36
+ <action>Generate the output artifact at {test_artifacts}/test-gap-analysis-{date}.md</action>
37
+ <action>Output must include the following FR-223 schema sections:
38
+ - Summary section with total count of stories analyzed, ACs scanned, gaps found, and overall coverage percentage
39
+ - Per-story gap table listing each story, its ACs, and their coverage status
40
+ - Coverage rate breakdown showing covered vs uncovered acceptance criteria
41
+ </action>
42
+ <action>If zero gaps are detected (all ACs have corresponding test cases):
43
+ Output "No coverage gaps detected" in the summary section with a gap count of 0 and coverage rate of 100%</action>
44
+ <template-output file="{test_artifacts}/test-gap-analysis-{date}.md">
45
+ Gap analysis report following FR-223 schema with summary count, per-story gap table, and coverage percentage.
46
+ </template-output>
47
+ </step>
48
+
49
+ <step n="6" title="Performance Validation">
50
+ <action>Verify workflow completed within the NFR-040 constraint of under 60 seconds</action>
51
+ <action>Log total execution time in the output footer</action>
52
+ </step>
53
+ </workflow>
@@ -0,0 +1,38 @@
1
+ name: test-gap-analysis
2
+ description: 'Scan test suite against requirements to identify untested or under-tested areas'
3
+ module: testing
4
+ agent: test-architect
5
+ execution_mode: planning
6
+ modes:
7
+ - coverage
8
+ - verification
9
+ default_mode: coverage
10
+ config_resolved: "{installed_path}/.resolved/test-gap-analysis.yaml"
11
+ config_source: "{project-root}/_gaia/testing/config.yaml"
12
+ installed_path: "{project-root}/_gaia/testing/workflows/test-gap-analysis"
13
+ instructions: "{installed_path}/instructions.xml"
14
+ validation: "{installed_path}/checklist.md"
15
+ traces_to:
16
+ - FR-221
17
+ - FR-223
18
+ - NFR-040
19
+ performance_constraints:
20
+ max_duration_seconds: 60 # NFR-040 — workflow must complete in under 60 seconds
21
+ input_file_patterns:
22
+ test_plan:
23
+ whole: "{test_artifacts}/test-plan.md"
24
+ load_strategy: "FULL_LOAD"
25
+ story_files:
26
+ whole: "{implementation_artifacts}/*.md"
27
+ load_strategy: "INDEX_GUIDED"
28
+ architecture:
29
+ whole: "{planning_artifacts}/architecture.md"
30
+ load_strategy: "INDEX_GUIDED"
31
+ sprint_status:
32
+ whole: "{implementation_artifacts}/sprint-status.yaml"
33
+ load_strategy: "FULL_LOAD"
34
+ output:
35
+ primary: "{test_artifacts}/test-gap-analysis-{date}.md"
36
+ on_error:
37
+ missing_file: "warn_and_continue"
38
+ unresolved_variable: "halt"
@@ -158,6 +158,8 @@ Commands:
158
158
  status Show installation info
159
159
 
160
160
  Options:
161
+ --branch <name> Clone from a specific branch
162
+ --staging Shorthand for --branch staging
161
163
  --yes Skip confirmation prompts
162
164
  --dry-run Show what would be done without making changes
163
165
  --verbose Show detailed progress
@@ -183,6 +185,8 @@ function main(deps) {
183
185
  const _join = (deps && deps.join) || join;
184
186
  const _mkdtemp = (deps && deps.mkdtempSync) || mkdtempSync;
185
187
  const _tmpdir = (deps && deps.tmpdir) || tmpdir;
188
+ const _isWindows = deps && typeof deps.isWindows === "boolean" ? deps.isWindows : IS_WINDOWS;
189
+ const _findBash = (deps && deps.findBash) || findBash;
186
190
 
187
191
  const args = process.argv.slice(2);
188
192
 
@@ -205,6 +209,32 @@ function main(deps) {
205
209
  fail(`Unknown command: ${command}\n Run 'npx gaia-framework --help' for usage.`);
206
210
  }
207
211
 
212
+ // ─── Branch flag parsing (E14-S1) ─────────────────────────────────────────
213
+ // Extract --branch / --staging before building the passthrough array.
214
+ // These flags control which git branch is cloned — they are consumed by the
215
+ // JS CLI and forwarded as --branch <name> to gaia-install.sh.
216
+ let branchValue = null;
217
+ const remaining = args.slice(0);
218
+
219
+ const branchIdx = remaining.indexOf("--branch");
220
+ const hasStaging = remaining.includes("--staging");
221
+
222
+ if (branchIdx >= 0 && hasStaging) {
223
+ fail("Cannot use --branch and --staging together. Use --branch staging instead.");
224
+ }
225
+
226
+ if (branchIdx >= 0) {
227
+ const valueIdx = branchIdx + 1;
228
+ if (valueIdx >= remaining.length || remaining[valueIdx].startsWith("--")) {
229
+ fail("Missing value for --branch flag. Usage: --branch <name>");
230
+ }
231
+ branchValue = remaining[valueIdx];
232
+ remaining.splice(branchIdx, 2);
233
+ } else if (hasStaging) {
234
+ branchValue = "staging";
235
+ remaining.splice(remaining.indexOf("--staging"), 1);
236
+ }
237
+
208
238
  // Ensure git is available
209
239
  ensureGit();
210
240
 
@@ -212,13 +242,13 @@ function main(deps) {
212
242
  tempDir = _mkdtemp(_join(_tmpdir(), "gaia-framework-"));
213
243
  // Resolve 8.3 short names to long paths on Windows (e.g., ELIASN~1 → Elias Nasser)
214
244
  // Node's realpathSync doesn't expand 8.3 names, so use PowerShell
215
- if (IS_WINDOWS) {
245
+ if (_isWindows) {
216
246
  try {
217
- const longPath = execSync(
247
+ const longPath = _exec(
218
248
  `powershell -NoProfile -Command "(Get-Item -LiteralPath '${tempDir}').FullName"`,
219
249
  { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
220
250
  ).trim();
221
- if (longPath && existsSync(longPath)) tempDir = longPath;
251
+ if (longPath && _exists(longPath)) tempDir = longPath;
222
252
  } catch {}
223
253
  }
224
254
 
@@ -235,8 +265,9 @@ function main(deps) {
235
265
 
236
266
  info("Cloning GAIA framework from GitHub...");
237
267
 
268
+ const branchCloneFlag = branchValue ? ` --branch ${branchValue}` : "";
238
269
  try {
239
- _exec(`git clone --depth 1 ${REPO_URL} "${tempDir}"`, {
270
+ _exec(`git clone --depth 1${branchCloneFlag} ${REPO_URL} "${tempDir}"`, {
240
271
  stdio: ["ignore", "ignore", "pipe"],
241
272
  });
242
273
  } catch (err) {
@@ -254,12 +285,17 @@ function main(deps) {
254
285
 
255
286
  // Build the shell command: inject --source pointing to the temp clone
256
287
  // so the shell script doesn't need to clone again
257
- const passthrough = args.slice(0);
288
+ const passthrough = remaining.slice(0);
258
289
  // Insert --source right after the command (convert to POSIX for bash on Windows)
259
290
  passthrough.splice(1, 0, "--source", toPosixPath(tempDir));
260
291
 
292
+ // Inject --branch flag for installer passthrough (E14-S1)
293
+ if (branchValue) {
294
+ passthrough.push("--branch", branchValue);
295
+ }
296
+
261
297
  // Locate bash (critical for Windows support)
262
- const bashPath = findBash();
298
+ const bashPath = _findBash();
263
299
  if (!bashPath) {
264
300
  fail(
265
301
  "bash is required but was not found.\n" +
@@ -273,12 +309,12 @@ function main(deps) {
273
309
  try {
274
310
  // Convert all passthrough args that look like paths (contain backslash or drive letter)
275
311
  const posixArgs = passthrough.map((a) =>
276
- IS_WINDOWS && /[\\:]/.test(a) && !a.startsWith("--") ? toPosixPath(a) : a
312
+ _isWindows && /[\\:]/.test(a) && !a.startsWith("--") ? toPosixPath(a) : a
277
313
  );
278
314
  const posixScript = toPosixPath(scriptPath);
279
315
 
280
316
  // Debug: on Windows, log the resolved paths if --verbose is passed
281
- if (IS_WINDOWS && args.includes("--verbose")) {
317
+ if (_isWindows && args.includes("--verbose")) {
282
318
  info(`Bash: ${bashPath} (${bashType})`);
283
319
  info(`Script (Windows): ${scriptPath}`);
284
320
  info(`Script (POSIX): ${posixScript}`);
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Derive bump label from PR title conventional commit prefix.
3
+ * Used by .github/workflows/pr-title-label.yml (E14-S11, ADR-025).
4
+ *
5
+ * @param {string} title - PR title
6
+ * @param {string} body - PR body
7
+ * @returns {{ label: string, type: string, breaking: boolean } | null}
8
+ * Returns the derived bump label info, or null if title doesn't match.
9
+ */
10
+
11
+ const TITLE_REGEX = /^(feat|fix|refactor|perf|test|docs|chore|ci|style)(\(.+\))?(!)?: .+$/;
12
+
13
+ const TYPE_TO_LABEL = Object.freeze({
14
+ feat: "bump:minor",
15
+ fix: "bump:patch",
16
+ perf: "bump:patch",
17
+ refactor: "bump:none",
18
+ test: "bump:none",
19
+ docs: "bump:none",
20
+ chore: "bump:none",
21
+ ci: "bump:none",
22
+ style: "bump:none",
23
+ });
24
+
25
+ function deriveBumpLabel(title, body) {
26
+ const match = title.match(TITLE_REGEX);
27
+ if (!match) return null;
28
+
29
+ const type = match[1];
30
+ const bang = match[3] === "!";
31
+ const bodyBreaking = typeof body === "string" && body.includes("BREAKING CHANGE");
32
+ const breaking = bang || bodyBreaking;
33
+
34
+ const label = breaking ? "bump:major" : TYPE_TO_LABEL[type];
35
+
36
+ return { label, type, breaking };
37
+ }
38
+
39
+ module.exports = deriveBumpLabel;
40
+ module.exports.TITLE_REGEX = TITLE_REGEX;
41
+ module.exports.TYPE_TO_LABEL = TYPE_TO_LABEL;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * PR bump label validation for staging merge enforcement.
3
+ * Used by .github/workflows/label-check.yml (E14-S6, ADR-025).
4
+ */
5
+
6
+ const VALID_BUMP_LABELS = Object.freeze(["bump:major", "bump:minor", "bump:patch", "bump:none"]);
7
+
8
+ /**
9
+ * Validate that exactly one bump:* label is present on a PR.
10
+ *
11
+ * @param {string[]} labels - Array of label names from the PR
12
+ * @returns {{ pass: boolean, message: string }} Validation result
13
+ */
14
+ function validateBumpLabels(labels) {
15
+ const bumpLabels = labels.filter((label) => VALID_BUMP_LABELS.includes(label));
16
+
17
+ if (bumpLabels.length === 0) {
18
+ return {
19
+ pass: false,
20
+ message: `No bump label found. Add one of: ${VALID_BUMP_LABELS.join(", ")}`,
21
+ };
22
+ }
23
+
24
+ if (bumpLabels.length > 1) {
25
+ return {
26
+ pass: false,
27
+ message: `Multiple bump labels found: ${bumpLabels.join(", ")}. Exactly one required.`,
28
+ };
29
+ }
30
+
31
+ return {
32
+ pass: true,
33
+ message: `Valid bump label: ${bumpLabels[0]}`,
34
+ };
35
+ }
36
+
37
+ module.exports = validateBumpLabels;
38
+ module.exports.VALID_BUMP_LABELS = VALID_BUMP_LABELS;
package/gaia-install.sh CHANGED
@@ -76,6 +76,7 @@ TARGET=""
76
76
  OPT_YES=false
77
77
  OPT_DRY_RUN=false
78
78
  OPT_VERBOSE=false
79
+ OPT_BRANCH=""
79
80
 
80
81
  # ─── Utility Functions ──────────────────────────────────────────────────────
81
82
 
@@ -95,7 +96,11 @@ clone_from_github() {
95
96
  fi
96
97
  TEMP_CLONE_DIR="$(mktemp -d "${TMPDIR:-/tmp}/gaia-framework-XXXXXX")"
97
98
  info "Cloning GAIA from GitHub..." >&2
98
- if git clone --depth 1 "$GITHUB_REPO" "$TEMP_CLONE_DIR" 2>/dev/null; then
99
+ local branch_args=()
100
+ if [[ -n "$OPT_BRANCH" ]]; then
101
+ branch_args=(--branch "$OPT_BRANCH")
102
+ fi
103
+ if git clone --depth 1 "${branch_args[@]}" "$GITHUB_REPO" "$TEMP_CLONE_DIR" 2>/dev/null; then
99
104
  success "Cloned to temporary directory" >&2
100
105
  else
101
106
  error "Failed to clone from $GITHUB_REPO"
@@ -233,7 +238,7 @@ copy_gaia_files() {
233
238
 
234
239
  # Fallback: cp -rp (preserves permissions like rsync -a)
235
240
  if [[ "$copy_done" == false ]] && command -v cp >/dev/null 2>&1; then
236
- if cp -rp "$src/_gaia/" "$dst/_gaia/" 2>/dev/null; then
241
+ if cp -rp "$src/_gaia/." "$dst/_gaia/" 2>/dev/null; then
237
242
  clean_resolved_yaml "$dst/_gaia"
238
243
  detail "Copied framework files using cp -rp (rsync unavailable)"
239
244
  copy_done=true
@@ -493,16 +498,18 @@ cmd_init() {
493
498
  fi
494
499
  fi
495
500
 
496
- # Step 8: Create custom skills directory
497
- step "Creating custom skills directory..."
498
- if [[ "$OPT_DRY_RUN" == true ]]; then
499
- detail "[dry-run] Would create: custom/skills/"
500
- else
501
- mkdir -p "$TARGET/custom/skills"
502
- fi
503
- if [[ -f "$source/custom/skills/README.md" ]]; then
504
- copy_if_missing "$source/custom/skills/README.md" "$TARGET/custom/skills/README.md"
505
- fi
501
+ # Step 8: Create custom directories (ADR-020: user-owned write targets)
502
+ step "Creating custom directories..."
503
+ for cdir in skills templates stakeholders; do
504
+ if [[ "$OPT_DRY_RUN" == true ]]; then
505
+ detail "[dry-run] Would create: custom/$cdir/"
506
+ else
507
+ mkdir -p "$TARGET/custom/$cdir"
508
+ fi
509
+ if [[ -f "$source/custom/$cdir/README.md" ]]; then
510
+ copy_if_missing "$source/custom/$cdir/README.md" "$TARGET/custom/$cdir/README.md"
511
+ fi
512
+ done
506
513
 
507
514
  # Step 9: Append GAIA entries to .gitignore
508
515
  step "Updating .gitignore..."
@@ -626,6 +633,35 @@ cmd_update() {
626
633
  "_config/manifest.yaml"
627
634
  )
628
635
 
636
+ # ─── Migrate .customize.yaml files before _gaia/ overwrite (E10-S19, FR-153) ──
637
+ # Copy user customize.yaml files from _gaia/_config/agents/ to custom/skills/
638
+ # before the framework overwrite replaces _gaia/ contents with defaults.
639
+ # Copy-only semantics: originals are left in place as fallback (AC2).
640
+ step "Migrating customize.yaml files to custom/skills/..."
641
+ if [[ "$OPT_DRY_RUN" != true ]]; then
642
+ mkdir -p "$TARGET/custom/skills"
643
+ fi
644
+ local migrated=0
645
+ for cust_file in "$TARGET/_gaia/_config/agents/"*.customize.yaml; do
646
+ [[ -f "$cust_file" ]] || continue
647
+ local cust_basename
648
+ cust_basename="$(basename "$cust_file")"
649
+ if [[ -f "$TARGET/custom/skills/$cust_basename" ]]; then
650
+ [[ "$OPT_VERBOSE" == true ]] && detail "Skipped (already exists): custom/skills/$cust_basename"
651
+ continue
652
+ fi
653
+ if [[ "$OPT_DRY_RUN" == true ]]; then
654
+ detail "[dry-run] Would migrate: _gaia/_config/agents/$cust_basename → custom/skills/$cust_basename"
655
+ else
656
+ cp "$cust_file" "$TARGET/custom/skills/$cust_basename"
657
+ info "[migrate] _gaia/_config/agents/$cust_basename → custom/skills/$cust_basename"
658
+ migrated=$((migrated + 1))
659
+ fi
660
+ done
661
+ if [[ "$migrated" -gt 0 ]]; then
662
+ detail "Migrated $migrated customize.yaml file(s) to custom/skills/"
663
+ fi
664
+
629
665
  step "Updating framework files..."
630
666
  local updated=0 skipped=0 changed=0
631
667
 
@@ -687,15 +723,17 @@ cmd_update() {
687
723
  fi
688
724
  done
689
725
 
690
- # Ensure custom skills directory exists (user-owned, never overwritten)
691
- if [[ "$OPT_DRY_RUN" == true ]]; then
692
- [[ ! -d "$TARGET/custom/skills" ]] && detail "[dry-run] Would create: custom/skills/"
693
- else
694
- mkdir -p "$TARGET/custom/skills"
695
- fi
696
- if [[ -f "$source/custom/skills/README.md" ]]; then
697
- copy_if_missing "$source/custom/skills/README.md" "$TARGET/custom/skills/README.md"
698
- fi
726
+ # Ensure custom directories exist (user-owned, never overwritten — ADR-020)
727
+ for cdir in skills templates stakeholders; do
728
+ if [[ "$OPT_DRY_RUN" == true ]]; then
729
+ [[ ! -d "$TARGET/custom/$cdir" ]] && detail "[dry-run] Would create: custom/$cdir/"
730
+ else
731
+ mkdir -p "$TARGET/custom/$cdir"
732
+ fi
733
+ if [[ -f "$source/custom/$cdir/README.md" ]]; then
734
+ copy_if_missing "$source/custom/$cdir/README.md" "$TARGET/custom/$cdir/README.md"
735
+ fi
736
+ done
699
737
 
700
738
  # Update slash commands (add new ones, update existing with backup)
701
739
  step "Updating slash commands..."
@@ -736,6 +774,29 @@ cmd_update() {
736
774
  fi
737
775
  fi
738
776
 
777
+ # ─── Post-install verification: check skill references in customize.yaml (E10-S19, AC4) ──
778
+ step "Verifying skill references in custom/skills/*.customize.yaml..."
779
+ for cust_file in "$TARGET/custom/skills/"*.customize.yaml; do
780
+ [[ -f "$cust_file" ]] || continue
781
+ local cust_name
782
+ cust_name="$(basename "$cust_file")"
783
+ # Extract source: values from customize.yaml (simple grep — no full YAML parser needed)
784
+ while IFS= read -r source_line; do
785
+ # Strip key prefix, leading whitespace, and surrounding quotes via parameter expansion
786
+ local ref_path="${source_line#*source:}"
787
+ ref_path="${ref_path#"${ref_path%%[![:space:]]*}"}" # trim leading whitespace
788
+ ref_path="${ref_path#[\"\']}" # trim leading quote
789
+ ref_path="${ref_path%[\"\']}" # trim trailing quote
790
+ [[ -z "$ref_path" ]] && continue
791
+ # Resolve relative paths against TARGET
792
+ local full_path="$TARGET/$ref_path"
793
+ [[ "$ref_path" == /* ]] && full_path="$ref_path"
794
+ if [[ ! -f "$full_path" ]]; then
795
+ warn "[warn] Broken skill reference in $cust_name: $ref_path not found"
796
+ fi
797
+ done < <(grep 'source:' "$cust_file" || true)
798
+ done
799
+
739
800
  # Summary
740
801
  echo ""
741
802
  if [[ -d "$backup_dir" ]]; then
@@ -828,6 +889,11 @@ cmd_validate() {
828
889
  [[ -d "$TARGET/docs/$dir" ]]; check "Docs: $dir" $?
829
890
  done
830
891
 
892
+ # Custom directories (ADR-020: user-owned write targets, ADR-026: stakeholder agents)
893
+ [[ -d "$TARGET/custom/skills" ]]; check "custom/skills/ exists" $?
894
+ [[ -d "$TARGET/custom/templates" ]]; check "custom/templates/ exists" $?
895
+ [[ -d "$TARGET/custom/stakeholders" ]]; check "custom/stakeholders/ exists" $?
896
+
831
897
  # Version
832
898
  local version
833
899
  version="$(extract_yaml_value "$TARGET/_gaia/_config/global.yaml" "framework_version")"
@@ -930,6 +996,7 @@ ${BOLD}Commands:${RESET}
930
996
 
931
997
  ${BOLD}Options:${RESET}
932
998
  --source <path> Local GAIA source (or clones from GitHub if omitted)
999
+ --branch <name> Clone from a specific branch
933
1000
  --yes Skip confirmation prompts
934
1001
  --dry-run Show what would be done without making changes
935
1002
  --verbose Show detailed progress
@@ -1003,6 +1070,14 @@ parse_args() {
1003
1070
  OPT_VERBOSE=true
1004
1071
  shift
1005
1072
  ;;
1073
+ --branch)
1074
+ if [[ -z "${2:-}" ]]; then
1075
+ error "--branch requires a branch name argument"
1076
+ exit 1
1077
+ fi
1078
+ OPT_BRANCH="$2"
1079
+ shift 2
1080
+ ;;
1006
1081
  --help|-h)
1007
1082
  usage
1008
1083
  exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaia-framework",
3
- "version": "1.65.1",
3
+ "version": "1.83.2",
4
4
  "description": "GAIA — Generative Agile Intelligence Architecture installer",
5
5
  "bin": {
6
6
  "gaia-framework": "./bin/gaia-framework.js"
File without changes
@@ -1,6 +0,0 @@
1
- test_name: "checkpoint-resume-reliability"
2
- date: "2026-03-24"
3
- result: "pass"
4
- observations: "All 10 test scenarios passed. Checkpoint validation (schema, checksums, legacy format), resume state reconstruction (happy path, error cases), and file modification detection (modified, deleted) all verified. 45 total tests: 31 unit + 14 Tier 2."
5
- runner: "Cleo (typescript-dev)"
6
- framework_version: "1.48.3"
@@ -1,14 +0,0 @@
1
- test_name: engine-scenarios
2
- date: "2026-03-22T00:00:00Z"
3
- result: pass
4
- observations: |
5
- All 6 ACs validated via 18 structural tests:
6
- - AC1: Step ordering verified in workflow.xml and dev-story instructions.xml (4 tests)
7
- - AC2: Checkpoint YAML schema validated with all required fields (3 tests)
8
- - AC3: Quality gate structure validated across all workflow configs (3 tests)
9
- - AC4: Variable resolution verified — all vars in known set, engine requires global.yaml (3 tests)
10
- - AC5: Execution mode switching definitions validated in workflow.xml (3 tests)
11
- - AC6: tier2-results directory exists, result YAML schema validated (2 tests)
12
- Zero regressions in existing Tier 1 tests (3486 passing).
13
- runner: vitest
14
- framework_version: "1.45.0"