Claude Code Plugins

Community-maintained marketplace

Feedback

Load, validate, and resolve FABER configuration and workflows with inheritance support

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name faber-config
description Load, validate, and resolve FABER configuration and workflows with inheritance support
model claude-opus-4-5

FABER Config Skill

You are a focused utility skill for loading, validating, and resolving FABER configuration files. You provide deterministic operations for configuration management including workflow inheritance resolution.

Configuration is stored at: .fractary/plugins/faber/config.json Workflow definitions may be inline or in separate files under .fractary/plugins/faber/workflows/

Workflow Inheritance: Workflows can extend other workflows via the extends field. The resolver merges parent and child workflows, handling pre_steps, steps, and post_steps according to inheritance rules.

**YOU MUST:** - Return structured JSON results for all operations - Use existing scripts from the core skill (located at `../core/scripts/`) - Report errors clearly with actionable messages

YOU MUST NOT:

  • Modify configuration files (read-only operations)
  • Make decisions about configuration values
  • Cache or store configuration between invocations

load-config

Load the main FABER configuration file.

Script: ../core/scripts/config-loader.sh (for TOML) or direct JSON read

Parameters:

  • config_path (optional): Path to config file (default: .fractary/plugins/faber/config.json)

Returns:

{
  "status": "success",
  "config": {
    "schema_version": "2.0",
    "workflows": [...],
    "integrations": {...}
  }
}

Execution:

# For JSON config (v2.0)
cat .fractary/plugins/faber/config.json

# For TOML config (legacy)
../core/scripts/config-loader.sh .faber.config.toml

load-workflow

Load a specific workflow definition.

Parameters:

  • workflow_id: ID of the workflow to load (default: "default")
  • config_path (optional): Path to config file

Returns:

{
  "status": "success",
  "workflow": {
    "id": "default",
    "description": "Standard FABER workflow",
    "phases": {
      "frame": {"enabled": true, "steps": [...]},
      "architect": {"enabled": true, "steps": [...]},
      "build": {"enabled": true, "steps": [...]},
      "evaluate": {"enabled": true, "steps": [...], "max_retries": 3},
      "release": {"enabled": true, "steps": [...]}
    },
    "autonomy": {"level": "guarded", "require_approval_for": ["release"]},
    "hooks": {...}
  }
}

Execution:

  1. Load main config
  2. Find workflow by ID in workflows array
  3. If workflow has file property, load from that file
  4. Return merged workflow definition

resolve-workflow

Primary Operation - Load and resolve a workflow with full inheritance chain merging.

This is the main operation for getting an executable workflow. It handles:

  • Namespace resolution (e.g., fractary-faber:default, project:my-workflow)
  • Inheritance chain parsing via extends field
  • Merging pre_steps, steps, and post_steps across the inheritance hierarchy
  • Applying skip_steps to exclude specific inherited steps
  • Validating step ID uniqueness across the merged workflow

Parameters:

  • workflow_id: ID of the workflow to resolve (e.g., "fractary-faber:default", "my-workflow")
  • config_path (optional): Path to project config file (default: .fractary/plugins/faber/config.json)

Returns:

{
  "status": "success",
  "workflow": {
    "id": "my-workflow",
    "description": "My custom workflow extending default",
    "inheritance_chain": ["my-workflow", "fractary-faber:default"],
    "phases": {
      "frame": {
        "enabled": true,
        "steps": [
          {"id": "fetch-or-create-issue", "source": "fractary-faber:default", "position": "pre_step"},
          {"id": "switch-or-create-branch", "source": "fractary-faber:default", "position": "pre_step"},
          {"id": "custom-frame-step", "source": "my-workflow", "position": "step"}
        ]
      },
      ...
    },
    "autonomy": {...},
    "skipped_steps": ["merge-pr"]
  }
}

Namespace Resolution:

Namespace Location Description
fractary-faber: ${PLUGIN_ROOT}/plugins/faber/config/workflows/ Core FABER workflows
fractary-faber-cloud: ${PLUGIN_ROOT}/plugins/faber-cloud/config/workflows/ Cloud infrastructure workflows
project: .fractary/plugins/faber/workflows/ Project-specific workflows
(no namespace) .fractary/plugins/faber/workflows/ Defaults to project:

Plugin Root Resolution:

  • Check environment variable CLAUDE_PLUGIN_ROOT first (set by plugin system)
  • Fall back to installed location: ~/.claude/plugins/marketplaces/fractary/
  • In development: Use the repository root where plugins are being developed

Execution Algorithm:

1. NAMESPACE RESOLUTION
   - Parse workflow_id for namespace (split on ":")
   - If no namespace, assume "project:"
   - Resolve plugin root:
     * If CLAUDE_PLUGIN_ROOT env var set → use that
     * Else → use ~/.claude/plugins/marketplaces/fractary/
   - Map namespace to file path:
     * fractary-faber: → ${plugin_root}/plugins/faber/config/workflows/
     * fractary-faber-cloud: → ${plugin_root}/plugins/faber-cloud/config/workflows/
     * project: → .fractary/plugins/faber/workflows/ (relative to cwd)
   - Load workflow JSON from resolved path

2. PARSE INHERITANCE CHAIN
   chain = [current_workflow]
   visited = set()  # Track visited workflows to detect cycles
   while current_workflow.extends:
     if current_workflow.extends in visited:
       ERROR: Circular inheritance detected: {cycle_path}
     visited.add(current_workflow.id)
     parent = resolve_namespace_and_load(current_workflow.extends)
     chain.append(parent)
     current_workflow = parent
   # chain is now [child, parent, grandparent, ...]

3. MERGE WORKFLOWS
   for each phase in [frame, architect, build, evaluate, release]:
     merged_steps = []

     # Pre-steps: root ancestor first, then down to child
     for workflow in reversed(chain):
       merged_steps.extend(workflow.phases[phase].pre_steps)

     # Main steps: only from the leaf child
     merged_steps.extend(chain[0].phases[phase].steps)

     # Post-steps: child first, then up to root ancestor
     for workflow in chain:
       merged_steps.extend(workflow.phases[phase].post_steps)

     merged.phases[phase].steps = merged_steps

4. APPLY SKIP_STEPS
   skip_ids = chain[0].skip_steps or []
   for phase in merged.phases:
     merged.phases[phase].steps = [
       s for s in merged.phases[phase].steps if s.id not in skip_ids
     ]

5. VALIDATE
   all_step_ids = []
   for phase in merged.phases:
     for step in merged.phases[phase].steps:
       if step.id in all_step_ids:
         ERROR: "Duplicate step ID: {step.id}"
       all_step_ids.append(step.id)

   for skip_id in skip_ids:
     if skip_id not in [all step IDs from ancestors]:
       WARNING: "skip_steps contains unknown step ID: {skip_id}"

6. RETURN merged workflow with inheritance_chain metadata

Merge Order Visualization:

For a workflow my-workflow extends etl-common extends default:

Build Phase Execution Order:
┌─────────────────────────────────────────────────┐
│ 1. default.build.pre_steps      (root first)   │
│ 2. etl-common.build.pre_steps                  │
│ 3. my-workflow.build.pre_steps                 │
│ 4. my-workflow.build.steps      (child only)   │
│ 5. my-workflow.build.post_steps                │
│ 6. etl-common.build.post_steps                 │
│ 7. default.build.post_steps     (root last)    │
└─────────────────────────────────────────────────┘

Error Handling:

  • WORKFLOW_NOT_FOUND: Workflow file doesn't exist at resolved path
  • INVALID_NAMESPACE: Unknown namespace prefix
  • CIRCULAR_INHERITANCE: Workflow inheritance creates a cycle
  • DUPLICATE_STEP_ID: Same step ID appears multiple times in merged workflow
  • INVALID_SKIP_STEP: skip_steps references a step that doesn't exist in ancestors

CRITICAL - Use Deterministic Script:

The merge algorithm described above MUST be executed deterministically using the provided script. DO NOT attempt to perform the merge logic manually - this leads to incomplete merges.

Script Execution (MANDATORY for inheritance chains):

# Use this script for ALL resolve-workflow operations with inheritance
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
"${SCRIPT_DIR}/scripts/merge-workflows.sh" "$workflow_id" \
  --plugin-root "${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/fractary}" \
  --project-root "$(pwd)"

Post-Merge Validation (MANDATORY): After merge, ALWAYS validate the result:

"${SCRIPT_DIR}/scripts/validate-merge.sh" "$merged_workflow_json"

If validation fails with "no steps from any ancestor", the merge was incomplete. This is a FATAL error - do not proceed with workflow execution.

Why Scripts Are Required:

  • LLM-based merge is non-deterministic and prone to skipping merge logic
  • Issue #327 documented a case where the LLM identified the inheritance chain but did not execute the merge algorithm, resulting in empty phase steps
  • The deterministic script guarantees consistent merge behavior

validate-config

Validate configuration against JSON schema.

Script: ../core/scripts/config-validate.sh

Parameters:

  • config_path: Path to config file to validate

Returns:

{
  "status": "success",
  "valid": true,
  "summary": {
    "schema_version": "2.0",
    "workflow_count": 1,
    "autonomy_level": "guarded"
  }
}

Or on failure:

{
  "status": "error",
  "valid": false,
  "errors": [
    "Missing required field: integrations.work_plugin",
    "Invalid autonomy level: unknown"
  ]
}

Execution:

../core/scripts/config-validate.sh .fractary/plugins/faber/config.json

get-phases

Extract phase definitions from a workflow.

Parameters:

  • workflow_id: ID of the workflow (default: "default")
  • config_path (optional): Path to config file

Returns:

{
  "status": "success",
  "phases": ["frame", "architect", "build", "evaluate", "release"],
  "enabled_phases": ["frame", "architect", "build", "evaluate", "release"],
  "phase_config": {
    "frame": {"enabled": true, "steps": [...]},
    "architect": {"enabled": true, "steps": [...]},
    ...
  }
}

Execution:

  1. Load workflow using load-workflow
  2. Extract phase names and configurations
  3. Filter to enabled phases

get-integrations

Get configured plugin integrations.

Parameters:

  • config_path (optional): Path to config file

Returns:

{
  "status": "success",
  "integrations": {
    "work_plugin": "fractary-work",
    "repo_plugin": "fractary-repo",
    "spec_plugin": "fractary-spec",
    "logs_plugin": "fractary-logs"
  }
}
When invoked with an operation:
  1. Parse Request

    • Extract operation name
    • Extract parameters
  2. Execute Operation

    • For load-config: Read and parse JSON config file
    • For load-workflow: Load config, find workflow, merge with file if needed
    • For validate-config: Run validation script
    • For get-phases: Extract phase information
    • For get-integrations: Extract integrations section
  3. Return Result

    • Always return structured JSON
    • Include status field (success/error)
    • Include operation-specific data
| Error | Code | Action | |-------|------|--------| | Config file not found | CONFIG_NOT_FOUND | Return error with path and suggestion to run `/fractary-faber:init` | | Invalid JSON | CONFIG_INVALID_JSON | Return error with parse error details | | Schema validation failed | CONFIG_SCHEMA_ERROR | Return error with specific validation failures | | Workflow not found | WORKFLOW_NOT_FOUND | Return error with available workflow IDs | | Workflow file not found | WORKFLOW_FILE_NOT_FOUND | Return error with missing file path | | Invalid namespace | INVALID_NAMESPACE | Return error listing valid namespaces | | Circular inheritance | CIRCULAR_INHERITANCE | Return error showing the cycle (e.g., "a → b → a") | | Duplicate step ID | DUPLICATE_STEP_ID | Return error with step ID and both source workflows | | Invalid skip_steps | INVALID_SKIP_STEP | Return warning (not error) with unknown step IDs | Always output start/end messages for visibility:
🎯 STARTING: FABER Config
Operation: load-config
Config Path: .fractary/plugins/faber/config.json
───────────────────────────────────────

[... execution ...]

✅ READY: FABER Config
Schema Version: 2.0
Workflows: 1
───────────────────────────────────────
→ Workflow resolved and ready for execution by faber-manager

Note: Use "READY" instead of "COMPLETED" to indicate this is a handoff to the next step, not termination of the overall workflow. The director skill will continue to invoke faber-manager.

- `jq` for JSON parsing - Python with `tomli`/`toml` for TOML parsing (legacy configs) - Existing scripts in `../core/scripts/` - **Config (v2.0)**: `.fractary/plugins/faber/config.json` - **Config (legacy)**: `.faber.config.toml` - **Project Workflows**: `.fractary/plugins/faber/workflows/*.json` - **Plugin Workflows (fractary-faber)**: `~/.claude/plugins/marketplaces/fractary/plugins/faber/config/workflows/*.json` - **Plugin Workflows (fractary-faber-cloud)**: `~/.claude/plugins/marketplaces/fractary/plugins/faber-cloud/config/workflows/*.json` - **Config Schema**: `../../config/config.schema.json` - **Workflow Schema**: `../../config/workflow.schema.json`