@ai-outfitter/outfitter 0.4.0 → 0.7.0

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 (87) hide show
  1. package/LICENSE.md +20 -50
  2. package/README.md +36 -247
  3. package/code/enterprise/LICENSE +35 -0
  4. package/code/enterprise/README.md +5 -0
  5. package/dist/agents/AdapterProfileControls.d.ts +2 -2
  6. package/dist/agents/AdapterProfileControls.js +8 -1
  7. package/dist/agents/AdapterProfileControls.js.map +1 -1
  8. package/dist/agents/AgentAdapter.d.ts +10 -0
  9. package/dist/agents/AgentLaunch.d.ts +6 -0
  10. package/dist/agents/AgentLaunch.js +89 -0
  11. package/dist/agents/AgentLaunch.js.map +1 -0
  12. package/dist/agents/claude/ClaudeAdapter.js +18 -3
  13. package/dist/agents/claude/ClaudeAdapter.js.map +1 -1
  14. package/dist/agents/pi/PiAdapter.js +159 -15
  15. package/dist/agents/pi/PiAdapter.js.map +1 -1
  16. package/dist/cli/commands/FirstRunWelcomeProfile.js +8 -5
  17. package/dist/cli/commands/FirstRunWelcomeProfile.js.map +1 -1
  18. package/dist/cli/commands/PiLoginLaunch.d.ts +8 -2
  19. package/dist/cli/commands/PiLoginLaunch.js +739 -30
  20. package/dist/cli/commands/PiLoginLaunch.js.map +1 -1
  21. package/dist/cli/commands/RunCommand.d.ts +20 -3
  22. package/dist/cli/commands/RunCommand.js +102 -20
  23. package/dist/cli/commands/RunCommand.js.map +1 -1
  24. package/dist/cli/commands/SetupCommand.d.ts +12 -2
  25. package/dist/cli/commands/SetupCommand.js +267 -70
  26. package/dist/cli/commands/SetupCommand.js.map +1 -1
  27. package/dist/cli/commands/SyncCommand.d.ts +8 -1
  28. package/dist/cli/commands/SyncCommand.js +2 -1
  29. package/dist/cli/commands/SyncCommand.js.map +1 -1
  30. package/dist/cli/commands/WelcomeCommand.d.ts +2 -1
  31. package/dist/cli/commands/WelcomeCommand.js +77 -70
  32. package/dist/cli/commands/WelcomeCommand.js.map +1 -1
  33. package/dist/cli/commands/assets/outfitter-ascii.txt +5 -0
  34. package/dist/cli/commands/profile/Command.d.ts +1 -0
  35. package/dist/cli/commands/profile/Command.js +3 -0
  36. package/dist/cli/commands/profile/Command.js.map +1 -1
  37. package/dist/cli/commands/profile/LintCommand.d.ts +19 -0
  38. package/dist/cli/commands/profile/LintCommand.js +123 -0
  39. package/dist/cli/commands/profile/LintCommand.js.map +1 -0
  40. package/dist/cli.js +8 -2
  41. package/dist/cli.js.map +1 -1
  42. package/dist/compositeProfile/StatePersistence.js +3 -0
  43. package/dist/compositeProfile/StatePersistence.js.map +1 -1
  44. package/dist/merge/ArrayMergePolicy.js.map +1 -1
  45. package/dist/merge/SettingsValueMerger.js.map +1 -1
  46. package/dist/profiles/Profile.d.ts +14 -1
  47. package/dist/profiles/Profile.js.map +1 -1
  48. package/dist/profiles/ProfileLoader.d.ts +4 -0
  49. package/dist/profiles/ProfileLoader.js +118 -17
  50. package/dist/profiles/ProfileLoader.js.map +1 -1
  51. package/dist/profiles/ProfileMerger.js +3 -0
  52. package/dist/profiles/ProfileMerger.js.map +1 -1
  53. package/dist/profiles/PromptIncludes.d.ts +32 -0
  54. package/dist/profiles/PromptIncludes.js +147 -0
  55. package/dist/profiles/PromptIncludes.js.map +1 -0
  56. package/dist/prompts/SystemPromptExport.d.ts +16 -0
  57. package/dist/prompts/SystemPromptExport.js +81 -0
  58. package/dist/prompts/SystemPromptExport.js.map +1 -0
  59. package/dist/schemas/profile.schema.json +38 -2
  60. package/dist/schemas/settings.schema.json +12 -0
  61. package/dist/settings/Settings.d.ts +5 -0
  62. package/dist/settings/Settings.js.map +1 -1
  63. package/dist/settings/SettingsLoader.js +3 -0
  64. package/dist/settings/SettingsLoader.js.map +1 -1
  65. package/dist/settings/SettingsMerger.js +8 -0
  66. package/dist/settings/SettingsMerger.js.map +1 -1
  67. package/package.json +23 -11
  68. package/skills/outfitter/SKILL.md +68 -0
  69. package/src/schemas/profile.schema.json +38 -2
  70. package/src/schemas/settings.schema.json +12 -0
  71. package/doc/.deepreview +0 -30
  72. package/doc/architecture.md +0 -855
  73. package/doc/controllable-elements.md +0 -162
  74. package/doc/file_structure.md +0 -133
  75. package/doc/integration_test_system.md +0 -214
  76. package/doc/specs/validating_requirements_with_rules.md +0 -55
  77. package/doc/state_writeback_strategy.md +0 -334
  78. package/requirements/OFTR-001-project-foundation.md +0 -53
  79. package/requirements/OFTR-002-settings.md +0 -65
  80. package/requirements/OFTR-003-profiles.md +0 -59
  81. package/requirements/OFTR-004-sync-and-setup.md +0 -67
  82. package/requirements/OFTR-005-run-and-composite-profile.md +0 -60
  83. package/requirements/OFTR-006-agent-adapters.md +0 -66
  84. package/requirements/OFTR-007-controllable-elements.md +0 -32
  85. package/requirements/OFTR-008-requirements-governance.md +0 -42
  86. package/requirements/OFTR-009-release-publishing.md +0 -34
  87. package/requirements/OFTR-010-onboarding-welcome.md +0 -39
@@ -1,162 +0,0 @@
1
- # Controllable Elements
2
-
3
- This document defines cross-agent-CLI concepts that Outfitter profiles may control.
4
- Pi is the first supported CLI, and Claude Code is supported as an additional adapter.
5
- Other CLIs may be added later while keeping the profile model generic.
6
-
7
- Status values:
8
-
9
- - **Supported**: Outfitter supports this control for the CLI.
10
- - **Roadmap**: the CLI appears to support this concept, but Outfitter does not support it yet.
11
- - **Unsupported**: the agent CLI cannot meaningfully support the concept or no known native mechanism exists.
12
-
13
- ## How to Read This Matrix
14
-
15
- A `Supported` entry means Outfitter can control that concept for the agent CLI through at least one native mechanism: a config-directory boundary, state-path placement, generated files, environment variables, command-line flags, or pass-through arguments.
16
- It does not always mean there is a one-to-one native CLI flag or that every generic profile selector has been mapped.
17
-
18
- For example, Claude Code session/project state lives under Claude's config home rather than a standalone `--session-dir` flag.
19
- Outfitter supports that session-directory concept for Claude by setting `CLAUDE_CONFIG_DIR` to the composite profile root, declaring Claude `projects/` state for persistence, and allowing `controls.session_directory` or `controls.claude.session_directory` to choose where that state is symlinked from.
20
- Likewise, Claude skills and commands are supported as native directories under the profiled `CLAUDE_CONFIG_DIR`, even though the generic `controls.skills` and `controls.prompt_template` selectors are not yet translated into Claude-specific selection flags.
21
-
22
- ## Defined Terms
23
-
24
- ### Agent Config Directory
25
-
26
- The root directory that stores agent-global configuration, credentials, installed resources, and related state.
27
-
28
- - Pi name: `PI_CODING_AGENT_DIR` / agent dir
29
- - Claude name: `CLAUDE_CONFIG_DIR` / Claude config home
30
-
31
- ### Session Directory
32
-
33
- The directory where conversation sessions, transcripts, or run state are stored.
34
-
35
- - Pi name: `PI_CODING_AGENT_SESSION_DIR` / `--session-dir`
36
- - Claude name: session/project state under `CLAUDE_CONFIG_DIR`, including `projects/` state managed by Outfitter state persistence
37
-
38
- ### Extensions
39
-
40
- Executable/plugin modules that add tools, providers, hooks, or runtime behavior.
41
-
42
- - Pi name: extensions, `--extension` / `-e`
43
- - Claude name: plugins via `--plugin-dir`
44
-
45
- ### Skills
46
-
47
- Reusable task instructions, workflows, or resource bundles exposed to the agent.
48
-
49
- - Pi name: skills, `--skill`
50
- - Claude name: skills under the Claude config directory; Outfitter can profile native Claude skills through `cli_specific/claude/skills`, but generic `skills` selection is not mapped yet
51
-
52
- ### Prompt Templates
53
-
54
- Named reusable prompts/templates available to the agent runtime.
55
-
56
- - Pi name: prompt templates, `--prompt-template`
57
- - Claude name: commands/prompts under the Claude config directory; Outfitter can profile native Claude commands through `cli_specific/claude/commands`, but generic `prompt_template` selection is not mapped yet
58
-
59
- ### System Prompt
60
-
61
- The primary instruction text supplied to the agent.
62
-
63
- - Pi name: `--system-prompt`, `SYSTEM.md`
64
- - Claude name: `--system-prompt`
65
-
66
- ### Appended System Prompt
67
-
68
- Additional instruction text layered onto the primary system prompt.
69
-
70
- - Pi name: `--append-system-prompt`, `APPEND_SYSTEM.md`
71
- - Claude name: `--append-system-prompt`
72
-
73
- ### Model Selection
74
-
75
- The selected provider/model and related inference options.
76
-
77
- - Pi name: `--provider`, `--model`, `--models`, `--thinking`
78
- - Claude name: `--model`, `--effort`
79
-
80
- ### Credentials and Environment
81
-
82
- Environment variables, API keys, auth files, and related secret material needed by providers or tools.
83
-
84
- - Pi name: provider env vars, `auth.json`, `--api-key`
85
- - Claude name: environment variables and config files under `CLAUDE_CONFIG_DIR`
86
-
87
- ### Tool Availability
88
-
89
- Configuration that enables, disables, or filters tools exposed to the agent.
90
-
91
- - Pi name: tool settings and extension-provided tools
92
- - Claude name: allowed/disallowed tools, roadmap adapter mapping
93
-
94
- ### Context Files
95
-
96
- Project or profile files automatically loaded into context.
97
-
98
- - Pi name: context files, `--no-context-files`
99
- - Claude name: project memory/context files, roadmap adapter mapping
100
-
101
- ### Theme / UI Presentation
102
-
103
- Terminal UI theme and presentation settings.
104
-
105
- - Pi name: themes, `--theme`, `--no-themes`
106
- - Claude name: UI/theme controls, roadmap adapter mapping
107
-
108
- ### Project Override Policy
109
-
110
- Whether project-local agent configuration is allowed, ignored, or constrained.
111
-
112
- - Pi name: project `.pi/` resources and settings
113
- - Claude name: project-local config, roadmap adapter mapping
114
-
115
- ### Working Directory
116
-
117
- The directory from which the inner agent CLI is launched.
118
-
119
- - Pi name: cwd/session cwd
120
- - Claude name: cwd/project directory
121
-
122
- ### Pass-through Arguments
123
-
124
- Arguments not recognized by Outfitter that are forwarded unmodified to the inner agent CLI.
125
-
126
- - Pi name: native pi CLI args
127
- - Claude name: native Claude CLI args
128
-
129
- ### Bootstrap Hook
130
-
131
- An early-startup customization used to register providers, tools, hooks, or additional runtime behavior.
132
-
133
- - Pi name: explicit bootstrap extension via `--extension` / `-e`
134
- - Claude name: startup hook/plugin mechanism, not mapped by Outfitter yet
135
-
136
- ## Support Matrix
137
-
138
- | Controllable Element | Pi | Claude |
139
- | --------------------------- | --------- | --------- |
140
- | Agent Config Directory | Supported | Supported |
141
- | Session Directory | Supported | Supported |
142
- | Extensions | Supported | Supported |
143
- | Skills | Supported | Supported |
144
- | Prompt Templates | Supported | Supported |
145
- | System Prompt | Supported | Supported |
146
- | Appended System Prompt | Supported | Supported |
147
- | Model Selection | Supported | Supported |
148
- | Credentials and Environment | Supported | Supported |
149
- | Tool Availability | Roadmap | Roadmap |
150
- | Context Files | Roadmap | Roadmap |
151
- | Theme / UI Presentation | Roadmap | Roadmap |
152
- | Project Override Policy | Roadmap | Roadmap |
153
- | Working Directory | Roadmap | Roadmap |
154
- | Pass-through Arguments | Supported | Supported |
155
- | Bootstrap Hook | Supported | Roadmap |
156
-
157
- ## Day-One Interpretation
158
-
159
- For v1, a Outfitter profile may describe all defined terms generically.
160
- The Pi adapter is the first implementation, and pi remains the default adapter.
161
- Adapter-specific overrides live under `controls.pi` and `controls.claude`; unsupported controls warn at runtime, and `--strict` makes those warnings fatal.
162
- For Claude Code, `skills/` and `commands/` are supported as native configuration directories inside the profiled `CLAUDE_CONFIG_DIR`; the generic `controls.skills` and `controls.prompt_template` selectors remain unmapped and warn if requested.
@@ -1,133 +0,0 @@
1
- # Outfitter File Structure
2
-
3
- This document records the key repository file and directory structure used by Outfitter.
4
- See `doc/architecture.md` for runtime file conventions such as `.outfitter` settings folders, profile folders, and generated composite profile directories.
5
-
6
- ## Repository Layout
7
-
8
- Outfitter is organized around clear TypeScript source boundaries, requirement documents, and scenario-based tests.
9
-
10
- ```text
11
- . # repository root
12
- ├── .deepreview # root DeepWork review rules for project-wide checks
13
- ├── .deepwork/ # DeepWork schemas and generated review instruction scratch files
14
- │ └── schemas/ # project-specific DeepSchema definitions
15
- ├── .github/ # GitHub automation configuration
16
- │ └── workflows/ # GitHub Actions workflows and local .deepreview rules
17
- ├── doc/ # architecture, design, and specification docs
18
- │ ├── .deepreview # documentation-specific DeepWork review rules
19
- │ ├── architecture.md # architectural rationale and runtime file conventions
20
- │ ├── controllable-elements.md # controllable element terminology and support matrix
21
- │ ├── file_structure.md # repository file structure overview
22
- │ ├── integration_test_system.md # fixture-backed integration test design
23
- │ ├── state_writeback_strategy.md # composite profile state persistence and writeback design
24
- │ └── specs/ # detailed supporting specs
25
- ├── doc_site/ # Nextra/Next.js documentation website
26
- │ ├── app/ # App Router pages, layout, and site styles
27
- │ ├── eslint.config.js # documentation site ESLint configuration
28
- │ ├── mdx-components.tsx # Nextra MDX component bridge
29
- │ ├── next.config.mjs # Next.js configuration wrapped by Nextra
30
- │ ├── package.json # documentation site package scripts and dependencies
31
- │ └── tsconfig.json # documentation site TypeScript configuration
32
- ├── requirements/ # formal OUTFITTER requirement documents
33
- │ ├── OFTR-001-project-foundation.md
34
- │ └── ...
35
- ├── .prettierignore # Prettier ignore rules
36
- ├── .prettierrc.json # Prettier formatting configuration
37
- ├── .snapperrc.toml # Snapper Markdown formatting configuration
38
- ├── plan.md # implementation plan
39
- ├── CONTRIBUTING.md # local install and contributor workflow guide
40
- ├── src/ # production TypeScript source
41
- │ ├── cli.ts # executable CLI entry point
42
- │ ├── cli/ # CLI parser construction and command registration
43
- │ │ ├── OutfitterCli.ts
44
- │ │ └── commands/ # command objects for non-trivial CLI behavior
45
- │ │ ├── CommandObject.ts
46
- │ │ ├── FirstRunWelcomeProfile.ts
47
- │ │ ├── PiLoginLaunch.ts
48
- │ │ ├── RunCommand.ts
49
- │ │ ├── SetupCommand.ts
50
- │ │ ├── SyncCommand.ts
51
- │ │ ├── WelcomeCommand.ts
52
- │ │ └── profile/ # profile command namespace and subcommands
53
- │ │ ├── Command.ts
54
- │ │ ├── CreateCommand.ts
55
- │ │ ├── ListCommand.ts
56
- │ │ └── Shared.ts
57
- │ ├── settings/ # settings loading and merging
58
- │ │ ├── Settings.ts
59
- │ │ ├── SettingsLoader.ts
60
- │ │ └── SettingsMerger.ts
61
- │ ├── profiles/ # profile loading, validation, resolution, and merging
62
- │ │ ├── Profile.ts
63
- │ │ ├── ProfileCache.ts
64
- │ │ ├── ProfileLoader.ts
65
- │ │ ├── ProfileMerger.ts
66
- │ │ └── ProfileSource.ts
67
- │ ├── merge/ # reusable deterministic value and array merge policy helpers
68
- │ │ ├── ArrayMergePolicy.ts
69
- │ │ └── SettingsValueMerger.ts
70
- │ ├── compositeProfile/ # generated runtime composite profile assembly and watching
71
- │ │ ├── CompositeProfile.ts
72
- │ │ ├── CompositeProfileAssembler.ts
73
- │ │ ├── CompositeProfileFile.ts
74
- │ │ ├── CompositeProfileTemplate.ts
75
- │ │ ├── CompositeProfileWatcher.ts
76
- │ │ └── StatePersistence.ts
77
- │ ├── agents/ # agent adapter boundary and CLI-specific adapters
78
- │ │ ├── AdapterProfileControls.ts
79
- │ │ ├── AdapterStatePaths.ts
80
- │ │ ├── AgentAdapter.ts
81
- │ │ ├── AgentRegistry.ts
82
- │ │ ├── LaunchResources.ts
83
- │ │ ├── ResourceIdentity.ts
84
- │ │ ├── pi/ # pi-specific adapter implementation
85
- │ │ │ ├── PiAdapter.ts
86
- │ │ │ ├── PiMcpConfig.ts
87
- │ │ │ ├── PiSettingsMergePolicy.ts
88
- │ │ │ └── PiCompositeProfileWriter.ts
89
- │ │ └── claude/ # Claude Code-specific adapter implementation
90
- │ │ ├── ClaudeAdapter.ts
91
- │ │ └── ClaudeCompositeProfileWriter.ts
92
- │ ├── schemas/ # JSON Schema artifacts for persisted formats
93
- │ │ ├── settings.schema.json
94
- │ │ ├── profile.schema.json
95
- │ │ ├── profile-source.schema.json
96
- │ │ └── SchemaDocument.ts
97
- │ └── validation/ # shared validation helpers
98
- │ ├── SchemaValidator.ts
99
- │ └── YamlDocument.ts
100
- ├── scripts/ # local development and formatting helper scripts
101
- │ ├── .deepreview # script-specific DeepWork review rules
102
- │ └── run-snapper.mjs # pinned Snapper binary downloader/runner
103
- ├── tests/ # automated tests
104
- │ ├── fixtures/ # reusable test fixtures
105
- │ │ ├── integration/ # fixture-backed integration scenarios, catalog, and local .deepreview
106
- │ │ └── scenarios/ # compact profile-resolution scenarios and expected outputs
107
- │ ├── integration/ # fixture-backed integration tests and harness helpers
108
- │ ├── setup.ts # Vitest global setup for quiet test-output guards
109
- │ ├── test-console.ts # shared console-output guard helpers for tests
110
- │ └── unit/ # unit tests grouped by functionality under test, including welcome and first-run tests
111
- ├── package-lock.json # locked npm dependency graph
112
- └── package.json # npm package metadata and scripts
113
- ```
114
-
115
- The exact layout may evolve, but these boundaries should stay recognizable.
116
-
117
- ## Test Fixtures
118
-
119
- Integration fixtures should live under `tests/fixtures/integration/` with full `home/`, `project/`, and optional `expected/` trees.
120
- Fixture-backed integration tests and shared harness helpers should live under `tests/integration/`.
121
-
122
- Scenario fixtures should live under `tests/fixtures/scenarios/`, for example:
123
-
124
- ```text
125
- tests/fixtures/scenarios/
126
- profile-cycle/
127
- profile-inheritance-chain/
128
- profile-missing-inheritance/
129
- profile-multiple-inheritance/
130
- profile-precedence/
131
- ```
132
-
133
- Each scenario should include realistic `.outfitter` folders and expected resolution output.
@@ -1,214 +0,0 @@
1
- # Integration Test System
2
-
3
- ## Purpose
4
-
5
- Outfitter uses fixture-backed integration tests to exercise real settings/profile directory trees rather than constructing every input inside individual tests.
6
- This is especially important for composite profile generation and state persistence because the effective runtime composite profile can be composed from multiple settings files, multiple profile sources, inherited profiles, adapter defaults, and CLI-specific profile state files.
7
-
8
- The core risk is that a generated composite profile can look correct in isolated unit tests while write-back/state-persistence behavior remains ambiguous or unsafe when the composite profile came from four or more durable sources.
9
- Integration fixtures make those source relationships visible and testable.
10
-
11
- ## Goals
12
-
13
- - Store realistic, pre-written Outfitter projects as fixture directories.
14
- - Copy each fixture into a temporary directory before the test mutates it.
15
- - Traverse fixtures through the same public loading, resolution, composite profile assembly, and run/write detection paths used by commands.
16
- - Assert the resulting composite profile shape, symlinks, generated files, argv/env launch plan, warnings, and durable write results.
17
- - Make source ownership explicit enough that tests prove Outfitter does not perform unsupported generic write-back into composed settings/profile inputs.
18
- - Keep fixtures reusable across tests and documented in the integration fixture catalog.
19
-
20
- ## Non-goals
21
-
22
- - Integration tests do not replace focused unit tests for schema validation, merge algorithms, adapter helpers, or path-safety checks.
23
- - Outfitter does not implement generic structured merge-back from generated composite profile files into settings or profiles.
24
- - Integration tests do not require real pi or Claude Code binaries.
25
- Tests inject fake launchers that read/write the composite profile.
26
- - Integration tests do not depend on the developer machine's real home directory, native CLI config, or network state.
27
-
28
- ## Repository layout
29
-
30
- ```text
31
- tests/
32
- integration/
33
- composite profile-generation.test.ts
34
- fixtureHarness.ts
35
- fixtures/
36
- integration/
37
- README.md
38
- trivial_repo_only_profile/
39
- README.md
40
- home/
41
- .outfitter/settings.yml
42
- .outfitter/profiles/default/profile.yml
43
- project/
44
- .outfitter/settings.yml
45
- .outfitter/profiles/repo-review/profile.yml
46
- expected/
47
- pi/
48
- composite profile-summary.json
49
- warnings.json
50
- ```
51
-
52
- The existing `tests/fixtures/scenarios/` directory contains small profile-resolution fixtures used by current unit tests.
53
- The integration framework does not replace or move those fixtures.
54
- End-to-end fixtures live in `tests/fixtures/integration/` because they model full home/project/cache/native trees and expected composite profile effects.
55
- When an existing scenario is useful for an integration test, copy or expand it into a new integration fixture instead of changing the existing unit-test fixture in place.
56
-
57
- Additional fixture-set PRs target this framework branch and add more directories under `tests/fixtures/integration/`.
58
-
59
- ## Fixture directory contract
60
-
61
- Each integration fixture is a complete synthetic filesystem tree.
62
- A fixture uses these top-level entries:
63
-
64
- | Path | Purpose |
65
- | ----------- | ------------------------------------------------------------------------------------------------------------ |
66
- | `README.md` | Human explanation of the scenario and the behavior it protects. |
67
- | `home/` | Synthetic user home directory passed to command execution. |
68
- | `project/` | Synthetic project directory passed to command execution. |
69
- | `expected/` | Expected composite profile summaries, symlink targets, warnings, launch args/env, and durable file contents. |
70
-
71
- A fixture may also include these top-level entries:
72
-
73
- | Path | Purpose |
74
- | --------- | ----------------------------------------------------------------------------------------------- |
75
- | `native/` | Explicit native CLI state tree for tests that map adapter fallbacks away from `home/`. |
76
- | `cache/` | Pre-seeded Outfitter cache, remote-profile cache, or adapter cache/tooling state when required. |
77
-
78
- Settings and profiles inside `home/` and `project/` use normal Outfitter paths such as `.outfitter/settings.yml`, `.outfitter/local/settings.yml`, and `.outfitter/profiles/<id>/profile.yml`.
79
- CLI-specific state lives under profile folders, for example `cli_specific/pi/settings.json` or `cli_specific/claude/settings.json`.
80
- Fixture names describe the user/project scenario rather than the adapter; adapter-specific expected output is nested under `expected/pi/`, `expected/claude/`, and similar directories.
81
-
82
- ## Harness responsibilities
83
-
84
- `tests/integration/fixtureHarness.ts` exports these helpers:
85
-
86
- - `copyFixtureToTemp(name)` copies `tests/fixtures/integration/<name>` into `mkdtemp` and returns an `IntegrationFixture` with `root`, `home`, `project`, `cache`, and `expected` paths.
87
- - `runFixture(fixture, options)` executes `executeRunCommand` with the copied home/project and the provided fake launcher.
88
- - `summarizePiComposite profile(fixture, composite profileRoot)` returns stable pi-specific facts about generated profile content and selected state symlinks.
89
- - `readExpectedJson(fixture, relativePath)` loads expected fixture output from the copied `expected/` tree.
90
- - `readFixtureText(fixture, relativePath)` reads a text file from the copied fixture root.
91
- - `cleanupIntegrationFixtures()` removes copied fixtures after each test.
92
-
93
- The harness normalizes absolute paths in expected output using tokens such as `<fixture>`, `<home>`, `<project>`, `<cache>`, and `<composite profile>`.
94
- This keeps expected files readable and independent of temporary directory names.
95
-
96
- ## Where mutations and assertions live
97
-
98
- The framework uses a split model:
99
-
100
- - **Fixture files encode inputs and expected stable outputs.
101
- ** The fixture directory contains settings, profiles, CLI-specific state files, and optional `expected/*.json` snapshots such as expected symlink targets, warnings, or final durable file contents.
102
- - **Test code encodes behavior.
103
- ** The Vitest test case chooses which fixture to run, defines the fake launcher's mutation script, and performs assertions against the copied fixture and any expected files.
104
-
105
- This keeps fixtures readable as normal Outfitter directory trees while keeping active behavior in TypeScript where it can use filesystem APIs, helper functions, and precise assertions.
106
- Do not hide executable test behavior in ad hoc YAML unless a future mutation-script format is deliberately added.
107
-
108
- For example:
109
-
110
- ```ts
111
- it('does not copy generated composed settings back to source layers', async () => {
112
- const fixture = copyFixtureToTemp('heavily_overridden_engineering');
113
-
114
- const result = await runFixture(fixture, {
115
- launcher(plan) {
116
- const composite profileRoot = composite profileRootFromLaunchPlan(plan);
117
- writeFileSync(join(composite profileRoot, 'outfitter', 'profile.json'), '{"mutated":true}\n');
118
- writeFileSync(join(composite profileRoot, 'unexpected.txt'), 'unknown write\n');
119
- return Promise.resolve(0);
120
- },
121
- });
122
-
123
- expect(result.warnings).toEqual(readExpectedJson(fixture, 'warnings.json'));
124
- expect(readFileSync(join(fixture.project, '.outfitter/settings.yml'), 'utf8')).toBe(
125
- readExpectedText(fixture, 'source-project-settings-after.yml'),
126
- );
127
- });
128
- ```
129
-
130
- A fixture can include optional expectation files like:
131
-
132
- ```text
133
- expected/
134
- composite profile-summary.json
135
- warnings.json
136
- durable-files-after.json
137
- source-project-settings-after.yml
138
- ```
139
-
140
- The test decides how much to snapshot.
141
- Use structured expected files for broad composite profile summaries and explicit inline assertions for the key write-back invariant the test protects.
142
-
143
- ## Test flow
144
-
145
- A typical integration test:
146
-
147
- 1. Copies a named fixture to a temporary directory.
148
- 2. Runs Outfitter command code with the copied `homeDirectory` and `projectDirectory`.
149
- 3. Uses a fake launcher defined in the Vitest test to inspect the composite profile while it exists.
150
- 4. Mutates declared or undeclared composite profile paths from that fake launcher when the scenario requires it.
151
- 5. Lets normal post-run state detection classify the mutations.
152
- 6. Asserts warnings/errors and durable source files after the command completes, using fixture `expected/` files where snapshots improve readability.
153
-
154
- Example fake-launcher responsibilities:
155
-
156
- ```text
157
- - Read the adapter config root from launch plan env, such as `PI_CODING_AGENT_DIR` or `CLAUDE_CONFIG_DIR`.
158
- - Assert generated composite profile files exist.
159
- - Assert declared persistent paths are symlinks to the expected profile/native/cache sources.
160
- - Write to declared state paths and unknown files according to the scenario.
161
- - Return exit code 0 unless the scenario is testing child failure handling.
162
- ```
163
-
164
- ## Composite profile assertions
165
-
166
- Integration tests assert stable behavior, not incidental implementation details.
167
- Useful composite profile assertions include:
168
-
169
- - selected adapter and profile id;
170
- - resolved profile stack order;
171
- - generated Outfitter metadata files;
172
- - generated adapter-specific files such as profile/settings payloads;
173
- - launch argv and environment;
174
- - symlink versus temporary materialization for each declared state path;
175
- - symlink target precedence: project-local profile, project profile, user profile, cache, then native fallback as applicable;
176
- - non-persistent baseline paths and detected writes;
177
- - warnings becoming fatal under `--strict`.
178
-
179
- ## Write-back/state-persistence focus
180
-
181
- The integration suite encodes the product rule from `doc/state_writeback_strategy.md`: Outfitter does not do generic post-run copy-back or structured merge-back.
182
- Durable writes happen only through declared state paths that have a persistent strategy, normally by symlink.
183
-
184
- Important cases:
185
-
186
- - A generated composite profile file composed from several settings/profile layers is mutated in the composite profile.
187
- Current behavior is explicit: the mutation is not generically copied back to any source layer unless that path is an adapter-declared symlink.
188
- - A state file supplied by a high-precedence profile is symlinked and receives writes directly.
189
- - A declared state path configured as `warn` or `error` is not persisted.
190
- - Unknown files written into the composite profile are classified by the adapter's `unknown` strategy and never silently persisted.
191
- - Cache-backed adapter paths, such as Pi `utilities/` and `bin/`, persist to the configured cache rather than to profile directories.
192
-
193
- ## Fixture authoring rules
194
-
195
- - Prefer realistic fixtures with one clear user/project story.
196
- - Name fixtures after that story, such as `trivial_repo_only_profile` or `heavily_overridden_engineering`, rather than after an adapter.
197
- - Include enough settings/profile layers to demonstrate precedence when the scenario is about merging.
198
- - Use obvious values such as `REMOTE_ONLY`, `USER_ONLY`, `REPO_ONLY`, `LOCAL_ONLY`, and `SHARED` so failed assertions identify the wrong layer immediately.
199
- - Keep expected output as JSON when possible so tests can diff structured facts.
200
- - Avoid timestamps, random IDs, host-specific paths, and real credentials.
201
- - Include a fixture-root `README.md` for every fixture set.
202
-
203
- ## Extension path
204
-
205
- The framework currently includes `trivial_repo_only_profile` as the first implemented fixture.
206
- Additional fixture-set PRs add the remaining cataloged scenarios under `tests/fixtures/integration/` and extend `tests/integration/composite profile-generation.test.ts` or add focused integration test files when needed.
207
-
208
- When adding or changing a fixture:
209
-
210
- 1. Add or update the fixture root `README.md` in the same change.
211
- 2. Add static fixture files under `home/`, `project/`, optional `cache/` or `native/`, and optional `expected/`.
212
- 3. Add a Vitest integration test that copies the fixture, runs through command code, and asserts the composite profile/write-back behavior.
213
- 4. Update `tests/fixtures/integration/INTEGRATION_TEST_FIXTURES.md` when the fixture's scenario, status, or purpose changes.
214
- 5. Run `npm run check-ci` before opening or updating the PR.
@@ -1,55 +0,0 @@
1
- ## Validating Requirements with Tests, DeepSchemas, and Review Rules
2
-
3
- Formal requirements use RFC 2119 keywords such as MUST, SHOULD, and MAY.
4
- Each requirement needs an appropriate validation mechanism.
5
-
6
- ### Use automated TypeScript tests for deterministic requirements
7
-
8
- Use tests when a requirement can be checked by exact values, paths, return values, emitted CLI arguments, or structured data.
9
-
10
- Examples:
11
-
12
- - `PI_CODING_AGENT_DIR` MUST be set to the selected profile directory → assert the generated launch environment contains that exact value.
13
- - A profile name MUST be rejected when it contains whitespace → assert validation returns an error.
14
- - The wrapper MUST pass `--extension` for configured bootstrap extensions → assert generated argv contains the expected flag and path.
15
-
16
- ### Use DeepSchemas for file-level contracts
17
-
18
- Use DeepSchemas when a file must satisfy structural or semantic constraints.
19
-
20
- - Named DeepSchemas are useful for classes of files, such as all requirement specs.
21
- - Anonymous DeepSchemas are useful when a rule governs one specific file.
22
- - Put exact structural checks in JSON Schema when the target file is structured data.
23
- - Put semantic RFC 2119 requirements in the DeepSchema `requirements` section when judgment is required.
24
-
25
- ### Use `.deepreview` rules for broad judgment-based policies
26
-
27
- Use DeepReview rules when the reviewer must compare changed files, requirements, tests, documentation, or conventions across multiple files.
28
-
29
- Examples:
30
-
31
- - Requirement files MUST follow the project requirement format.
32
- - Tests that claim requirement coverage MUST use durable traceability comments.
33
- - Documentation MUST stay consistent with TypeScript source behavior.
34
-
35
- ### Anti-patterns
36
-
37
- Do not use fragile keyword tests for judgment requirements.
38
- A test such as `expect(text).toContain("safe")` does not prove that a prompt or policy meaningfully enforces safety.
39
-
40
- Do not spend reviewer judgment on facts that TypeScript tests or JSON Schema can verify exactly.
41
-
42
- ### Test traceability comment format
43
-
44
- Tests that validate a formal requirement should use a durable comment immediately before the relevant `it(...)`, `test(...)`, or `describe(...)` block:
45
-
46
- ```ts
47
- // THIS TEST VALIDATES A HARD REQUIREMENT (OFTR-008.3).
48
- // YOU MUST NOT MODIFY THIS TEST UNLESS THE REQUIREMENT CHANGES.
49
- it('rejects stale requirement traceability comments', () => {
50
- // ...
51
- });
52
- ```
53
-
54
- Comments should explain durable policy intent.
55
- They should not reference source line numbers, pull request numbers, or one-time migration context.