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.
- package/.claude/commands/gaia-create-stakeholder.md +20 -0
- package/.claude/commands/gaia-test-gap-analysis.md +17 -0
- package/CLAUDE.md +102 -1
- package/README.md +2 -2
- package/_gaia/_config/global.yaml +5 -1
- package/_gaia/_config/lifecycle-sequence.yaml +20 -0
- package/_gaia/_config/skill-manifest.csv +2 -0
- package/_gaia/_config/workflow-manifest.csv +3 -1
- package/_gaia/core/engine/workflow.xml +11 -1
- package/_gaia/core/protocols/review-gate-check.xml +29 -1
- package/_gaia/core/workflows/party-mode/steps/step-01-agent-loading.md +60 -9
- package/_gaia/creative/workflows/problem-solving/checklist.md +64 -14
- package/_gaia/creative/workflows/problem-solving/instructions.xml +367 -22
- package/_gaia/creative/workflows/problem-solving/workflow.yaml +31 -1
- package/_gaia/dev/agents/_base-dev.md +7 -1
- package/_gaia/dev/skills/_skill-index.yaml +9 -0
- package/_gaia/dev/skills/figma-integration.md +296 -0
- package/_gaia/lifecycle/knowledge/brownfield/config-contradiction-scan.md +137 -0
- package/_gaia/lifecycle/knowledge/brownfield/dead-code-scan.md +179 -0
- package/_gaia/lifecycle/knowledge/brownfield/test-execution-scan.md +209 -0
- package/_gaia/lifecycle/skills/document-rulesets.md +91 -6
- package/_gaia/lifecycle/templates/brownfield-scan-doc-code-prompt.md +219 -0
- package/_gaia/lifecycle/templates/brownfield-scan-hardcoded-prompt.md +169 -0
- package/_gaia/lifecycle/templates/brownfield-scan-integration-seam-prompt.md +127 -0
- package/_gaia/lifecycle/templates/brownfield-scan-runtime-behavior-prompt.md +141 -0
- package/_gaia/lifecycle/templates/brownfield-scan-security-prompt.md +440 -0
- package/_gaia/lifecycle/templates/gap-entry-schema.md +282 -0
- package/_gaia/lifecycle/templates/infra-prd-template.md +356 -0
- package/_gaia/lifecycle/templates/platform-prd-template.md +431 -0
- package/_gaia/lifecycle/templates/prd-template.md +70 -0
- package/_gaia/lifecycle/templates/story-template.md +22 -1
- package/_gaia/lifecycle/workflows/2-planning/create-ux-design/instructions.xml +52 -3
- package/_gaia/lifecycle/workflows/4-implementation/add-feature/checklist.md +1 -1
- package/_gaia/lifecycle/workflows/4-implementation/add-feature/instructions.xml +2 -3
- package/_gaia/lifecycle/workflows/4-implementation/add-stories/checklist.md +5 -0
- package/_gaia/lifecycle/workflows/4-implementation/add-stories/instructions.xml +73 -1
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/checklist.md +25 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/instructions.xml +79 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-stakeholder/workflow.yaml +22 -0
- package/_gaia/lifecycle/workflows/4-implementation/create-story/instructions.xml +11 -1
- package/_gaia/lifecycle/workflows/4-implementation/retrospective/instructions.xml +21 -1
- package/_gaia/lifecycle/workflows/4-implementation/retrospective/workflow.yaml +1 -1
- package/_gaia/lifecycle/workflows/4-implementation/validate-story/instructions.xml +11 -0
- package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/checklist.md +12 -0
- package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/instructions.xml +248 -4
- package/_gaia/lifecycle/workflows/anytime/brownfield-onboarding/workflow.yaml +1 -0
- package/_gaia/testing/workflows/test-gap-analysis/checklist.md +8 -0
- package/_gaia/testing/workflows/test-gap-analysis/instructions.xml +53 -0
- package/_gaia/testing/workflows/test-gap-analysis/workflow.yaml +38 -0
- package/bin/gaia-framework.js +44 -8
- package/bin/helpers/derive-bump-label.js +41 -0
- package/bin/helpers/validate-bump-labels.js +38 -0
- package/gaia-install.sh +96 -21
- package/package.json +1 -1
- package/_gaia/_memory/tier2-results/.gitkeep +0 -0
- package/_gaia/_memory/tier2-results/checkpoint-resume-2026-03-24.yaml +0 -6
- package/_gaia/_memory/tier2-results/engine-scenarios-2026-03-22.yaml +0 -14
|
@@ -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"
|
package/bin/gaia-framework.js
CHANGED
|
@@ -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 (
|
|
245
|
+
if (_isWindows) {
|
|
216
246
|
try {
|
|
217
|
-
const longPath =
|
|
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 &&
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
|
497
|
-
step "Creating custom
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
691
|
-
|
|
692
|
-
[[
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
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"
|