Skip to content

feat: add --fit axis for seniority-tier composition#61

Merged
danmestas merged 1 commit into
mainfrom
feat/issue-60-fit-axis
May 14, 2026
Merged

feat: add --fit axis for seniority-tier composition#61
danmestas merged 1 commit into
mainfrom
feat/issue-60-fit-axis

Conversation

@danmestas

Copy link
Copy Markdown
Owner

Summary

Adds fit as a fourth composition primitive alongside outfit / cut / accessory. Fits express seniority tier (junior-engineer / engineer / senior-engineer / staff-engineer) and compose as a strict overlay between outfit and cut in the prose-union layer order: outfit → fit → cut → accessory. Skill/agent/hook union remains order-independent set algebra.

Closes #60.
Companion wardrobe PR: danmestas/wardrobe#122 (introduces the 4 fits content side).

What changed

  • Schema: FitSchema mirrors CutSchema (overlay shape) with type: 'fit'.
  • Discovery: new src/lib/fit.ts (findFit / listAllFits, 3-tier project → user → builtin); fits/ added to discover.ts; fit.md filename convention.
  • CLI: --fit <name> wired through suit <harness>, suit up, suit prepare. Singleton — duplicate --fit flags error at parse time. Unknown fit names error with "Available: ..." list.
  • Resolver: fit layers between outfit and cut for skill_include/exclude, include-block force-includes, enable/disable globals, and category intersection. cutPrompt unions fit body + cut body when both present; preserves cut-only output byte-for-byte for back-compat.
  • Adapters (claude-code / codex / gemini / pi): emit [] for type: fit — same as cut/accessory, composed at resolve time.
  • Lockfile: optional fit field on resolution block, omitted when null so pre-fit lockfiles remain bit-identical.
  • Introspection: suit list fits and suit show fit <name>.

Test plan

  • Unit: fits/<name>/fit.md resolves identically to a cut shape (FitSchema parsing, 3-tier discovery, project-scope wins, throws with available names listing)
  • Unit: --fit <unknown> errors with available names
  • Unit: duplicate --fit flags error as singleton violation
  • Unit: fit.skill_include rescues a category-dropped skill
  • Unit: fit.skill_exclude drops an outfit-included skill
  • Unit: fit.include.skills force-rescues a skill
  • Unit: outfit → fit → cut prose unioning in cutPrompt
  • Unit: progressive category intersection across outfit + fit + cut
  • Integration (against /Users/dmestas/projects/wardrobe): SUIT_CONTENT_PATH=$WD suit list fits → 4 fits returned
  • Integration: SUIT_CONTENT_PATH=$WD suit show fit engineer → renders manifest + body
  • Integration: SUIT_CONTENT_PATH=$WD suit prepare --outfit code --fit junior-engineer --target claude --dry-run → 49 deduped files emitted
  • Integration: SUIT_CONTENT_PATH=$WD suit up --outfit code --fit junior-engineer --cut executing writes lockfile with fit: junior-engineer
  • Local CI mirror: npm ci && npm run typecheck && npm run build && npm link && npm test — 584/584 pass (557 existing + 27 new)

Back-compat

  • RunUpArgs.fit and RunPrepareArgs.fit are optional — pre-fit callers compile unchanged.
  • Lockfile resolution block omits fit when null — existing test fixtures bit-equal.
  • cutPrompt preserves cutBody ?? '' byte-for-byte when only cut is supplied.
  • ManifestSchema discriminated union adds FitSchema as a new branch — non-fit manifests parse unchanged.
  • v1 lockfile schema accepts both with-fit and without-fit shapes.

Introduces `fit` as a fourth composition primitive alongside outfit, cut,
and accessory. Fits express seniority tier (junior-engineer / engineer /
senior-engineer / staff-engineer) and compose as an overlay between outfit
and cut in the prose-union layer order: outfit → fit → cut → accessory.
Skill/agent/hook union remains order-independent set algebra.

- Register `fit` as a known component type alongside outfit/cut/accessory
- Add FitSchema (same shape as CutSchema with type literal 'fit')
- New src/lib/fit.ts mirrors cut.ts (findFit / listAllFits, 3-tier discovery)
- Wire `--fit <name>` CLI flag through suit <harness>, suit up, suit prepare;
  enforce singleton (duplicates error at parse time)
- Add `fits/` to content-root scan with fit.md filename convention
- Resolver: fit layers between outfit and cut for skill_include/exclude,
  include block force-includes, enable/disable globals, and category
  intersection. cutPrompt unions fit body + cut body when both present;
  preserves cut-only byte-for-byte for back-compat.
- Adapters (claude-code / codex / gemini / pi): emit [] for `type: fit`
  (same as cut/accessory — composed at resolve time, not per-component)
- Lockfile: optional `fit` field in resolution block, omitted when null
  for back-compat with pre-fit lockfiles
- Suit introspection: `suit list fits` and `suit show fit <name>`
- Validate: TAXONOMY cross-ref + body size limits apply to fits

Tests added:
- src/tests/fit.test.ts — schema validation, 3-tier discovery, listing,
  precedence, composition (skill_include rescue, skill_exclude drop,
  include.skills force-include, fit+cut prose layering, category intersection)
- ac-cli.test.ts: --fit parsing, singleton enforcement, value-required errors

Closes #60
@danmestas danmestas merged commit 8fd1ea5 into main May 14, 2026
1 check passed
@danmestas danmestas deleted the feat/issue-60-fit-axis branch May 14, 2026 16:17
@danmestas danmestas mentioned this pull request May 14, 2026
danmestas added a commit that referenced this pull request May 14, 2026
Two composition-axis extensions shipped between 0.14.0 and 0.15.0:

- `fit` axis (seniority tier) — #61, closes #60
- `include` block on outfits (parity with cuts/accessories) — #63, closes #62

Both additions are opt-in; no breaking changes. CHANGELOG updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add --fit axis for seniority-tier composition

1 participant