Customization — packs, matrix, and custom phases
What you need: A working project-state/ substrate. Familiarity with phase gates helps.
project-state's behavior comes from configuration, not code. Six skills are profile-driven — their behavior changes based on which YAML profiles are loaded. Profiles are shipped by compliance packs. The reporting matrix determines what gets generated and when. Phase presets define the lifecycle. All of it is editable YAML.
What a pack contains
A pack is a directory under packs/ with a fixed structure:
packs/agile-default/
├── manifest.yaml # Pack identity and version
├── README.md # What this pack does
├── profiles/
│ └── review-meeting.yaml # Profile for project-review-meeting
└── reporting-matrix-defaults.yaml # Entries to seed into the matrix
Here's the agile-default pack's manifest:
pack:
id: "agile-default"
name: "Agile Engineering Defaults"
version: "0.1.0-starter"
compatible_core: ">=2.0,<3.0"
description: |
Pack for engineering teams running Scrum/Kanban with release trains.
Configures sprint cadence, retro templates, release-phase model.
Does not assume a funder or customer; pair with another pack for those.
maturity: "starter"
provides:
profiles:
- review-meeting
- phase-gate
reporting_matrix_defaults: true
The provides.profiles array lists which skill profiles this pack ships. The reporting_matrix_defaults: true flag means the scaffolder will merge this pack's default matrix entries into the project's reporting matrix during setup.
Pack composition
Packs compose. Load agile-default for sprint ceremonies and sred-canada for SR&ED tracking on the same project:
# manifest.yaml
project:
packs_loaded: ["agile-default", "sred-canada"]
Each pack provides profiles for different skills. agile-default provides review-meeting (sprint retrospective format). sred-canada provides sred-tracker and sred-reviewer (SR&ED work capture and claim review). They don't conflict because they address different skills and different stakeholder groups.
When two packs provide profiles for the same skill (e.g., both ship a phase-gate.yaml), the profiles merge: criteria from both packs apply. If there's a direct conflict (same criterion ID, different check), the pack loaded later wins.
Editing the reporting matrix
The reporting matrix is a YAML file you edit directly. Here's a before-and-after example — adding a monthly technical brief:
Before:
entries:
- id: monday-tracker-email
stakeholder_group: internal.team
report: "Monday tracker email"
cadence: { kind: weekly, day: monday, lead_time_hours: 8 }
format: md
surface: blog
generator: project-status-reporter
After:
entries:
- id: monday-tracker-email
stakeholder_group: internal.team
report: "Monday tracker email"
cadence: { kind: weekly, day: monday, lead_time_hours: 8 }
format: md
surface: blog
generator: project-status-reporter
- id: monthly-technical-brief
stakeholder_group: internal.team
report: "Monthly technical brief"
cadence: { kind: monthly, day_of_month: 1, lead_time_hours: 24 }
format: md
surface: gmail
generator: project-status-reporter
description: |
Monthly summary of technical progress, architecture decisions,
and upcoming technical milestones. Sent as Gmail draft to team lead.
The orchestrator picks up the new entry on its next invocation. No restart, no config reload — it reads the file every time.
Each entry needs five fields: stakeholder_group (who), report (what), cadence (when), surface (where), generator (which skill). The format and description fields are optional but useful for clarity.
Writing a custom phase preset
Phase presets are YAML files in templates/phase-presets/. To create a custom lifecycle:
# templates/phase-presets/research-to-production.yaml
id: research-to-production
name: "Research to Production Pipeline"
phases:
- id: "01-research"
name: "Research"
gate_in: []
gate_out:
- id: lit_review
label: "Literature review published"
check: "documents/published/lit-review-*.md exists"
- id: hypothesis_defined
label: "Research hypothesis documented"
check: "decisions/*-hypothesis.yaml exists"
- id: "02-prototype"
name: "Prototype"
gate_in:
- id: research_done
label: "Research phase completed"
gate_out:
- id: prototype_validated
label: "Prototype validated against acceptance criteria"
- id: stakeholder_demo
label: "Stakeholder demo completed"
- id: "03-scale"
name: "Scale"
gate_in:
- id: prototype_accepted
label: "Prototype accepted by stakeholders"
gate_out:
- id: performance_targets
label: "Performance targets met at scale"
- id: "04-production"
name: "Production"
gate_in:
- id: scale_validated
label: "Scale validation complete"
gate_out: []
Reference it in your manifest:
phases:
preset: "research-to-production"
current_phase: "01-research"
The phase-gate skill reads your custom preset and enforces its gates. No code changes needed.
Authoring a new pack
A new pack follows the same directory structure. The full authoring guide is in docs/PACK-AUTHORING.md. The essential steps:
- Create
packs/<your-pack-id>/manifest.yamlwith pack identity - Add profile YAMLs under
profiles/for each skill you want to configure - Add
reporting-matrix-defaults.yamlwith matrix entries for your stakeholder groups - Set
maturity: "starter"until the pack has been used on a real project
The six profile-driven skills that packs can configure:
| Skill | Profile controls |
|---|---|
project-review-meeting | Meeting format, agenda template, cadence |
project-funder-reporting | Report structure, required sections, submission format |
project-external-comms | Review pipeline, approval chain, audience tiers |
project-ip-tracker | Disclosure format, recipient, filing workflow |
project-phase-gate | Gate criteria additions/overrides per phase |
project-archive | Closeout checklist, retention policy, handoff artifacts |
Each skill looks for its profile at packs/<pack-id>/profiles/<skill-name>.yaml. If the profile exists, the skill loads it. If not, the skill uses its built-in defaults.
The design system as extension point
The interactive scaffolding design system (ProgressBar, OptionCard, FormField, ToggleCard, NavRow) is documented as a fifth extension point. Custom skills that present wizard-like flows can reuse the same components for visual consistency across both Coworker and Claude Code surfaces. The component specifications are in the scaffolder's SKILL.md.
Next step
The substrate is configured, the packs are loaded, the matrix is tuned. But where does the information that feeds all of this come from in the first place? Harvesting intelligence shows how project-state pulls signals from Slack, Gmail, Google Docs, and scsiwyg into the inbox for triage and classification.