diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index a6410c6..7b901c9 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "codex-sdlc", - "version": "0.5.0", + "version": "0.6.0", "description": "Run evidence-backed software delivery across one or more repositories with Codex.", "author": { "name": "vimihi", diff --git a/CHANGELOG.md b/CHANGELOG.md index f2e29e8..2a33793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to codex-sdlc are documented here. +## 0.6.0 - 2026-09-24 + +- Add the `sdlc` entry skill for `/sdlc --save-my-token` and `/sdlc --normal` chat shorthand, with explicit `$sdlc` invocation support. +- Add project presets through `configure-agents --save-my-token` and `--normal`. Token-saving mode inherits PM, selects `gpt-6-sol/high` for BA, and `gpt-6-luna/xhigh` for backend, web/mobile frontend, and QC. Normal mode restores inheritance for those delivery roles. +- Persist presets for future runs while preserving existing run snapshots, optional AI Product Owner settings, and unrelated project configuration. Support dry-run previews and reject conflicting mode/custom settings. + ## 0.4.3 - 2026-09-11 - Shorten every default prompt to satisfy the Plugins Directory 128-character maximum. diff --git a/README.md b/README.md index 6ddfd5e..a730c0f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ PM coordinates the stages and reviews each handoff. Frontend work follows the re ### A workflow you can resume -Seven Codex skills and a repository-local CLI keep tasks, evidence, blockers, and decisions in your repository. The next session can continue from recorded state. +Eight Codex skills and a repository-local CLI keep tasks, evidence, blockers, and decisions in your repository. The next session can continue from recorded state. | What you need | What codex-sdlc provides | | --- | --- | @@ -83,7 +83,7 @@ Start a codex-sdlc feature delivery for: . To continue later, ask Codex to resume the existing run. Its manifest records the current tasks, completed work, and remaining decisions. -**Version note:** Per-role model routing and AI Product Owner review require both the **0.5.0 plugin and runtime**. Check your installed plugin version; updating the plugin does not upgrade an existing project's runtime. See the [upgrade guide](docs/getting-started.md#upgrade-an-existing-project). +**Version note:** Project model modes require both the **0.6.0 plugin and runtime**. Per-role routing and AI Product Owner review remain available from 0.5.0. Check your installed plugin version; updating the plugin does not upgrade an existing project's runtime. See the [upgrade guide](docs/getting-started.md#upgrade-an-existing-project).
Prefer the CLI? Preview a Next.js setup @@ -91,7 +91,7 @@ To continue later, ask Codex to resume the existing run. Its manifest records th Run this against an existing web repository: ```sh -npx --yes codex-sdlc@0.5.0 init \ +npx --yes codex-sdlc@0.6.0 init \ --root /absolute/path/to/web --name example-web \ --applications web --web-root . --web-preset nextjs \ --dry-run @@ -106,7 +106,7 @@ node .sdlc/runtime.cjs doctor node .sdlc/runtime.cjs validate-config ``` -For a global CLI installation, use `npm install --global codex-sdlc@0.5.0`. +For a global CLI installation, use `npm install --global codex-sdlc@0.6.0`.
@@ -143,6 +143,7 @@ Keep Codex's inherited models, or configure a model and reasoning effort for eac | Skill | Responsibility | | --- | --- | +| `sdlc` | Apply project model modes and route feature requests to PM. | | `sdlc-setup` | Initialize, diagnose, configure, upgrade, roll back, and uninstall. | | `sdlc-pm` | Coordinate delivery, review handoffs, and prepare the final package. | | `sdlc-ba` | Define requirements, acceptance criteria, and traceability. | @@ -151,7 +152,7 @@ Keep Codex's inherited models, or configure a model and reasoning effort for eac | `sdlc-qc` | Independently verify acceptance coverage, defects, and retests. | | `sdlc-po` | Provide an optional advisory Product Owner review after QC. | -For example, with a compatible 0.5.0 plugin and runtime: +For example, with a compatible plugin and runtime: ```text Use Astra for frontend, Luna for backend, and Sol for PM and QC. @@ -163,6 +164,19 @@ Selections must be available on your Codex host. New runs snapshot the settings; → [Model configuration, fallbacks, and AI Product Owner review](docs/agent-models.md) +### Project model modes + +Version 0.6.0 adds these chat shortcuts: + +```text +/sdlc --save-my-token +/sdlc --normal +``` + +Token-saving mode keeps PM inherited, uses Sol (`gpt-6-sol`) with high reasoning for BA, and Luna (`gpt-6-luna`) with extra-high reasoning for backend, web/mobile frontend, and QC. Normal mode restores inheritance for those roles. Both save to the current project's `.sdlc/project.yaml` for every new run; existing runs and optional AI Product Owner settings are preserved. + +These are skill-handled chat shortcuts. In clients that reject custom slash commands, invoke `$sdlc --save-my-token` or `$sdlc --normal`. Both plugin and runtime must be 0.6.0 or newer. See [project mode details and CLI equivalents](docs/agent-models.md#project-model-modes). + ## What stays in your repository ```text @@ -186,7 +200,7 @@ codex-sdlc does not operate a hosted service or send repository content to a cod Preview a project upgrade before applying it: ```sh -npx --yes codex-sdlc@0.5.0 upgrade \ +npx --yes codex-sdlc@0.6.0 upgrade \ --root /absolute/path/to/coordinator --dry-run ``` @@ -200,10 +214,10 @@ Applied upgrades create backups. Rollback checks managed-file integrity before r | --- | --- | | [Getting started](docs/getting-started.md) | [CLI setup and lifecycle reference](docs/cli-reference.md) | | [Multi-repository workspaces](docs/multi-repository.md) | [Role models and dispatch records](docs/agent-models.md) | -| [What's new in 0.5.0](docs/releases/0.5.0.md) | [Release history](https://github.com/prime-vimihi/codex-sdlc/releases) | +| [What's new in 0.6.0](docs/releases/0.6.0.md) | [Release history](https://github.com/prime-vimihi/codex-sdlc/releases) | | [Support](SUPPORT.md) | [Contributing](CONTRIBUTING.md) | -Current verification includes macOS CI, the automated runtime tests, plugin and skill validation, and a live PM dispatch smoke test. Native Linux/Windows qualification and a complete delivery test spanning multiple models remain pending. See the [0.5.0 validation notes](docs/releases/0.5.0.md#validation). +Current verification includes automated runtime tests, plugin and skill validation, and independent built-CLI checks. Native Linux/Windows qualification and a complete delivery test spanning multiple models remain pending. See the [0.6.0 validation notes](docs/releases/0.6.0.md#validation). ## Build with us diff --git a/assets/framework.yaml b/assets/framework.yaml index 887e807..b7ea56d 100644 --- a/assets/framework.yaml +++ b/assets/framework.yaml @@ -1,7 +1,7 @@ schema_version: 1 framework: name: codex-sdlc - version: 0.5.0 + version: 0.6.0 run_schema_version: 1 project_schema_version: 2 installation: diff --git a/docs/agent-models.md b/docs/agent-models.md index d422540..b6a5a69 100644 --- a/docs/agent-models.md +++ b/docs/agent-models.md @@ -1,6 +1,49 @@ # Choose a model for each SDLC role -Available in codex-sdlc 0.5.0. Both the plugin and the repository runtime must support this version. Updating the plugin does not upgrade existing projects; upgrade the runtime first as described in the [getting started guide](getting-started.md). +Custom role settings are available in codex-sdlc 0.5.0. The project mode shortcuts below require both the 0.6.0 plugin and runtime. Updating the plugin does not upgrade existing projects; upgrade the runtime as described in the [getting started guide](getting-started.md). + +## Project model modes + +In your initialized project's Codex task, send: + +```text +/sdlc --save-my-token +``` + +The new `sdlc` skill interprets this chat shorthand and saves the following project configuration: + +| Role | Model | Reasoning | +| --- | --- | --- | +| PM | Inherited | Inherited | +| BA | `gpt-6-sol` | `high` | +| Backend | `gpt-6-luna` | `xhigh` (extra-high) | +| Frontend, including web and mobile | `gpt-6-luna` | `xhigh` (extra-high) | +| QC | `gpt-6-luna` | `xhigh` (extra-high) | + +To restore normal inherited models: + +```text +/sdlc --normal +``` + +Normal mode removes model, reasoning, and fallback overrides for PM, BA, backend, frontend, and QC. It does not restore custom models that existed before token-saving mode. Both commands preserve optional AI Product Owner configuration and all other project settings. + +Each selection is stored in the coordinator's `.sdlc/project.yaml` and applies to **every new run** until you change it. Other projects and existing runs are unaffected. A mode-only request does not start a feature run. In a multi-repository workspace, run it in the coordinator to cover its mapped applications. An uninitialized project needs setup first. + +Codex's explicit skill syntax is `$sdlc --save-my-token` or `$sdlc --normal`; use it if your client reserves slash commands for built-in actions. The plugin handles `/sdlc` as prompt shorthand rather than registering a native slash command. See [official skill invocation guidance](https://learn.chatgpt.com/docs/build-skills). + +The equivalent runtime commands, from the coordinator, are: + +```sh +node .sdlc/runtime.cjs configure-agents --save-my-token +node .sdlc/runtime.cjs configure-agents --normal +``` + +Add `--dry-run --json` to preview without saving. Use `--root /absolute/path/to/coordinator` to select a project explicitly. The two modes are mutually exclusive and cannot be combined with individual role or PO configuration flags; make custom changes in a separate invocation. Applying the same preset again keeps the same settings. + +The presets require `gpt-6-sol/high` and `gpt-6-luna/xhigh` support on the user's host. Configuration saves the requested policy; dispatch still validates availability. No fallback is added, and an unavailable model or effort stops dispatch. Token-saving mode selects these models; actual token usage depends on the work. + +## Custom role settings Ask Codex: diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 0802aa8..b8a07d3 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -15,11 +15,25 @@ npm pack Install the published CLI with: ```sh -npm install --global codex-sdlc@0.5.0 +npm install --global codex-sdlc@0.6.0 ``` ## Project setup modes +### Project model modes + +After initialization, the 0.6.0 runtime accepts: + +```sh +node .sdlc/runtime.cjs configure-agents --save-my-token --dry-run --json +node .sdlc/runtime.cjs configure-agents --save-my-token +node .sdlc/runtime.cjs configure-agents --normal +``` + +Both commands accept `--root `. Token-saving mode uses inherited PM, `gpt-6-sol/high` for BA, and `gpt-6-luna/xhigh` for backend, web/mobile frontend, and QC. Normal mode removes those five roles' overrides. Optional PO configuration is preserved. The choice persists for new runs in this project; existing runs keep their policies. Mode flags cannot be combined with one another or individual agent/PO settings, and are supported on `configure-agents`, not `init` or `start`. See [model modes](agent-models.md#project-model-modes) for chat invocation and compatibility. + +### Application setup + The initializer configures an existing repository; it does not generate application source code. Each selected application root must already exist. Initialize a web-only Next.js project: @@ -98,9 +112,9 @@ Use the `generic` application preset or `none` database preset when a listed pre Install the generated tarball, preview the bounded changes, and then initialize. During local testing, pin the generated repository launcher to the tarball: ```sh -npm install --global ./codex-sdlc-0.5.0.tgz -codex-sdlc init --root /path/to/project --name example --applications web --web-root . --web-preset nextjs --runtime-spec file:/absolute/path/codex-sdlc-0.5.0.tgz --dry-run -codex-sdlc init --root /path/to/project --name example --applications web --web-root . --web-preset nextjs --runtime-spec file:/absolute/path/codex-sdlc-0.5.0.tgz +npm install --global ./codex-sdlc-0.6.0.tgz +codex-sdlc init --root /path/to/project --name example --applications web --web-root . --web-preset nextjs --runtime-spec file:/absolute/path/codex-sdlc-0.6.0.tgz --dry-run +codex-sdlc init --root /path/to/project --name example --applications web --web-root . --web-preset nextjs --runtime-spec file:/absolute/path/codex-sdlc-0.6.0.tgz cd /path/to/project node .sdlc/runtime.cjs restore ``` @@ -119,8 +133,8 @@ The installer preserves existing `AGENTS.md` and `.gitignore` content, refuses c Preview and apply an upgrade with the new runtime package pinned into the repository: ```sh -codex-sdlc upgrade --root /path/to/project --runtime-spec file:/absolute/path/codex-sdlc-0.5.0.tgz --dry-run -codex-sdlc upgrade --root /path/to/project --runtime-spec file:/absolute/path/codex-sdlc-0.5.0.tgz +codex-sdlc upgrade --root /path/to/project --runtime-spec file:/absolute/path/codex-sdlc-0.6.0.tgz --dry-run +codex-sdlc upgrade --root /path/to/project --runtime-spec file:/absolute/path/codex-sdlc-0.6.0.tgz cd /path/to/project node .sdlc/runtime.cjs restore node .sdlc/runtime.cjs doctor diff --git a/docs/getting-started.md b/docs/getting-started.md index 9ae2b4b..cea3c3d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -14,7 +14,7 @@ codex-sdlc combines a Codex skills plugin with a deterministic npm CLI. The plug Open the repository that should own `.sdlc` and start a new Codex task: ```text -Initialize codex-sdlc for this existing repository. Explain the setup choices and show the dry run before applying changes. If the CLI is unavailable, use codex-sdlc@0.5.0 through npx. +Initialize codex-sdlc for this existing repository. Explain the setup choices and show the dry run before applying changes. If the CLI is unavailable, use codex-sdlc@0.6.0 through npx. ``` Tell Codex which applications already exist and where they live. Supported presets are Go for backend, Next.js for web, Flutter for mobile, PostgreSQL for the primary database, and Redis for cache. Codex will inspect the repository, show the proposed files, apply the same command without `--dry-run`, restore the pinned project runtime, and run diagnostics. @@ -34,7 +34,7 @@ Tell Codex which applications already exist and where they live. Supported prese Preview first: ```sh -npx --yes codex-sdlc@0.5.0 init --root /absolute/path/to/web --name example-web \ +npx --yes codex-sdlc@0.6.0 init --root /absolute/path/to/web --name example-web \ --applications web --web-root . --web-preset nextjs --dry-run ``` @@ -54,7 +54,7 @@ One checkout is the coordinator and owns `.sdlc`. The reserved repository ID `co This example uses the frontend checkout as the coordinator and maps a backend elsewhere: ```sh -npx --yes codex-sdlc@0.5.0 init \ +npx --yes codex-sdlc@0.6.0 init \ --root /absolute/path/to/frontend \ --name example-platform \ --workspace-mode multi-repository \ @@ -75,22 +75,22 @@ node .sdlc/runtime.cjs doctor node .sdlc/runtime.cjs validate-config ``` -Only the coordinator receives `.sdlc`. The plugin skills remain in each user's Codex plugin cache. Another contributor binds their local checkout paths with `npx --yes codex-sdlc@0.5.0 configure --root --repo =` and reruns `doctor`. +Only the coordinator receives `.sdlc`. The plugin skills remain in each user's Codex plugin cache. Another contributor binds their local checkout paths with `npx --yes codex-sdlc@0.6.0 configure --root --repo =` and reruns `doctor`. -Choose single-repository or multi-repository mode before starting delivery. Version 0.5.0 does not automatically convert an initialized single-repository project to multi-repository mode. Preserve existing runs before changing topology. +Choose single-repository or multi-repository mode before starting delivery. Version 0.6.0 does not automatically convert an initialized single-repository project to multi-repository mode. Preserve existing runs before changing topology. ## Choose agent models (optional) Ask Codex: `Configure frontend to use Astra, backend to use Luna, and PM and QC to use Sol.` The setup skill maps your choices to model IDs supported by your Codex host and stores them in the coordinator’s `.sdlc/project.yaml`. Unspecified roles inherit from Codex. -You can also ask: `Enable an advisory AI Product Owner review using Sol.` The review runs after QC; final acceptance remains your decision. See [Choose a model for each role](agent-models.md) for exact commands, explicit fallbacks, and resetting settings. These options require the 0.5.0 plugin and runtime. Existing runs keep their original model settings. +You can also ask: `Enable an advisory AI Product Owner review using Sol.` The review runs after QC; final acceptance remains your decision. See [Choose a model for each role](agent-models.md) for exact commands, explicit fallbacks, and resetting settings. Custom role settings require plugin/runtime 0.5.0 or newer. Project presets (`$sdlc --save-my-token` and `$sdlc --normal`) require 0.6.0. Existing runs keep their original model settings. ## Upgrade an existing project After installing the new plugin version, preview the project runtime upgrade: ```sh -npx --yes codex-sdlc@0.5.0 upgrade --root /absolute/path/to/coordinator --dry-run +npx --yes codex-sdlc@0.6.0 upgrade --root /absolute/path/to/coordinator --dry-run ``` Run the same command without `--dry-run`, then run `node .sdlc/runtime.cjs restore`, `doctor`, and `validate-config` from the coordinator. The plugin update supplies skills; the project upgrade supplies commands and schemas. diff --git a/docs/marketplace-submission.md b/docs/marketplace-submission.md index 07dbd0e..8bd9ddf 100644 --- a/docs/marketplace-submission.md +++ b/docs/marketplace-submission.md @@ -66,6 +66,14 @@ Set up and coordinate resumable software delivery across one repository or multi - Expected behavior: Setup previews and saves the selected role models in the coordinator. A new run snapshots them. PM sends actual model parameters when launching task-only agents, including PM-owned tasks, and records returned agent IDs. It leaves unreported actual model metadata null. - Expected result: Role-specific dispatch records match the requested models. PO-001 produces a cited advisory review after QC; final human acceptance stays pending. +### 7. Save and reset project model modes + +- User prompt: `$sdlc --save-my-token` in an initialized disposable project. +- Expected behavior: Preview and save inherited PM, `gpt-6-sol/high` for BA, and `gpt-6-luna/xhigh` for backend, web/mobile frontend, and QC in `.sdlc/project.yaml`. Preserve PO settings. A mode-only request does not start a feature run; every subsequently started run snapshots the preset. +- User prompt: `$sdlc --normal` in the same project. +- Expected behavior: Remove model, effort, and fallback overrides for the five delivery roles. Preserve existing run snapshots and optional PO settings. New runs inherit their delivery-role models. +- Fixture: A disposable initialized project using plugin/runtime 0.6.0. The host must advertise the requested models and efforts to exercise actual dispatch; saving and previewing configuration need no model dispatch. + ## Negative tests ### 1. Reject an out-of-authority write @@ -96,9 +104,14 @@ Set up and coordinate resumable software delivery across one repository or multi - Scenario: PO-001 recommends readiness for human review, but the user has not accepted delivery. - Expected behavior: The final package includes the recommendation and keeps human acceptance pending. The po agent must not call approval-decision or product-owner-decision or impersonate the user. +### 6. Reject conflicting project modes + +- User prompt: `$sdlc --save-my-token --normal`. +- Expected behavior: Explain that the flags conflict and leave project configuration unchanged. The equivalent runtime command fails before writing. + ## Release notes -Version 0.5.0 adds per-role model and reasoning settings for PM, BA, backend, frontend, QC, and advisory AI Product Owner review. The runtime snapshots settings into each new run, resolves explicit fallbacks from host capabilities, and records actual dispatch IDs with unknown actual model metadata left unreported. The new sdlc-po skill reviews acceptance coverage after QC; final acceptance remains a human decision. Existing runs and repository topology remain compatible. +Version 0.6.0 adds the sdlc entry skill and persistent project model presets. `/sdlc --save-my-token` inherits PM, selects Sol/high for BA, and Luna/extra-high for backend, web/mobile frontend, and QC. `/sdlc --normal` restores inherited models for those roles. Explicit `$sdlc` invocation is supported when a client reserves slash commands. Settings apply to every new run while preserving existing run policies, optional AI Product Owner settings, and other projects. Both CLI presets support dry-run previews and reject conflicting settings. Final acceptance remains human. ## Portal prerequisites @@ -117,12 +130,12 @@ Version 0.5.0 adds per-role model and reasoning settings for PM, BA, backend, fr Submit the skills-only plugin from the release source tree. The bundle root contains `plugin.json`, `skills/`, `.codex-plugin/plugin.json`, `README.md`, `docs/getting-started.md`, `docs/agent-models.md`, and the referenced brand assets. It requires no MCP server, authentication configuration, demo credentials, or network allowlist. -Build the upload ZIP and npm tarball from the reviewed 0.5.0 source. The prepared artifacts are under `build/public-submission/0.5.0/`; verify their hashes using that directory’s `SHA256SUMS` before upload. +Build the upload ZIP and npm tarball from the reviewed 0.6.0 source. The prepared artifacts are under `build/public-submission/0.6.0/`; verify their hashes using that directory’s `SHA256SUMS` before upload. ## Release order -1. Tag the reviewed source revision as `v0.5.0` and publish the GitHub release using `docs/releases/0.5.0.md`. -2. Wait for `.github/workflows/publish.yml` to publish `codex-sdlc@0.5.0` to npm with provenance, then verify that the public registry serves 0.5.0. Do not submit the plugin update until npm serves 0.5.0. +1. Tag the reviewed source revision as `v0.6.0` and publish the GitHub release using `docs/releases/0.6.0.md`. +2. Wait for `.github/workflows/publish.yml` to publish `codex-sdlc@0.6.0` to npm with provenance, then verify that the public registry serves 0.6.0. Do not submit the plugin update until npm serves 0.6.0. 3. Open the existing codex-sdlc listing in the OpenAI plugin submission portal, create a version update, keep the type **Skills only**, and upload the prepared skills bundle and listing logo. 4. Copy the listing, prompts, tests, availability, and release notes from this document; complete publisher identity and policy attestations. 5. Submit for review. After approval, explicitly publish the approved version to the universal Plugins Directory. diff --git a/docs/releases/0.6.0.md b/docs/releases/0.6.0.md new file mode 100644 index 0000000..c1499ae --- /dev/null +++ b/docs/releases/0.6.0.md @@ -0,0 +1,37 @@ +# codex-sdlc 0.6.0 + +Save a project-wide model preset for every new SDLC run with `/sdlc --save-my-token`, and return to inherited models with `/sdlc --normal`. + +## Changes + +- Add the `sdlc` entry skill for project mode changes and routing feature requests to PM. +- Token-saving mode inherits PM, uses `gpt-6-sol` with `high` reasoning for BA, and `gpt-6-luna` with `xhigh` reasoning for backend, web/mobile frontend, and QC. +- Normal mode removes model, effort, and fallback overrides for those five roles. It restores inheritance rather than earlier custom settings. +- Store settings in the coordinator's `.sdlc/project.yaml` for every future run. Preserve existing run snapshots, other projects, and optional AI Product Owner settings. +- Add `configure-agents --save-my-token` and `configure-agents --normal`, including dry-run previews and conflict rejection. No schema migration is required. + +The slash forms are skill-handled chat shorthand, not registered native slash commands. If a client rejects them, use `$sdlc --save-my-token` or `$sdlc --normal`. Host model availability is checked at dispatch; the preset adds no implicit fallback or guarantee of token savings. + +## Upgrade + +Update the plugin to 0.6.0, then upgrade the coordinator's repository runtime separately: + +```sh +npx --yes codex-sdlc@0.6.0 upgrade --root /absolute/path/to/coordinator --dry-run +``` + +Apply the same command without `--dry-run`, then run `node .sdlc/runtime.cjs restore`, `doctor`, and `validate-config` from the coordinator. Apply a mode before starting a new run: + +```sh +node .sdlc/runtime.cjs configure-agents --save-my-token +# Return future runs to inherited delivery-role models: +node .sdlc/runtime.cjs configure-agents --normal +``` + +See [project model modes](https://github.com/prime-vimihi/codex-sdlc/blob/v0.6.0/docs/agent-models.md#project-model-modes) for role settings and behavior. + +## Validation + +All 51 automated tests, TypeScript checks, the build, and skill validation passed locally. An independent built-CLI check exercised both previews, exact saved settings, future run snapshots, mobile routing, normal reset, existing-run preservation, and invalid targets or conflicting flags. The npm and local marketplace bundles include the new entry skill. + +Actual model dispatch and chat shorthand execution were not exercised for this change. Native Linux and Windows qualification and a complete delivery across multiple models remain deferred. diff --git a/package-lock.json b/package-lock.json index fab28a7..aa82961 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "codex-sdlc", - "version": "0.5.0", + "version": "0.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "codex-sdlc", - "version": "0.5.0", + "version": "0.6.0", "license": "Apache-2.0", "dependencies": { "ajv": "8.20.0", diff --git a/package.json b/package.json index 9811c11..99ea7a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codex-sdlc", - "version": "0.5.0", + "version": "0.6.0", "description": "Evidence-backed SDLC runtime for one or more repositories with Codex", "type": "module", "license": "Apache-2.0", diff --git a/plugin.json b/plugin.json index 8af098a..375c69f 100644 --- a/plugin.json +++ b/plugin.json @@ -1,7 +1,7 @@ { "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "codex-sdlc", - "version": "0.5.0", + "version": "0.6.0", "description": "Run evidence-backed software delivery across one or more repositories with Codex.", "author": { "name": "vimihi", diff --git a/skills/sdlc-pm/SKILL.md b/skills/sdlc-pm/SKILL.md index 9926c46..7f9d3ca 100644 --- a/skills/sdlc-pm/SKILL.md +++ b/skills/sdlc-pm/SKILL.md @@ -5,6 +5,8 @@ description: Use when starting, coordinating, resuming, reviewing, or finalizing # SDLC Project Orchestrator +For `/sdlc --save-my-token` or `/sdlc --normal` (including `$sdlc` invocation), first use the [sdlc entry point](../sdlc/SKILL.md) to apply the requested project model preset. A mode-only request does not start a run. New runs snapshot saved settings; resumed runs keep their existing policy. + Make repository state—not an agent claim—the source of delivery truth. Read [role contract](references/role-contract.md), [orchestration contract](references/orchestration-contract.md), [approval contract](references/approval-contract.md), and [final review contract](references/final-review-contract.md). For a delegated assignment with `execution_mode: task-only`, perform only the named PM task in its allowed scope and return its artifacts and review conclusions to the dispatcher. Wait for activation if instructed. Do not create another PM or run the complete orchestration loop. diff --git a/skills/sdlc-setup/SKILL.md b/skills/sdlc-setup/SKILL.md index 24642ed..3a42753 100644 --- a/skills/sdlc-setup/SKILL.md +++ b/skills/sdlc-setup/SKILL.md @@ -11,7 +11,7 @@ Resolve the repository the user selected before running a command. Read its `AGE For a new setup, explain briefly that the installed plugin supplies Codex skills while the npm CLI creates and operates `.sdlc`. Plugin skills stay in Codex's plugin cache; initialization does not create `.agents/skills` in the project. -Confirm Node.js satisfies the version declared by codex-sdlc. Run `codex-sdlc --version` when the executable is available and use it only when it reports 0.5.0. If it is unavailable or differs, run the release-pinned CLI through `npx --yes codex-sdlc@0.5.0`; do not require a global installation. Let the environment request approval if downloading the package requires network access. +Confirm Node.js satisfies the version declared by codex-sdlc. Run `codex-sdlc --version` when the executable is available and use it only when it reports 0.6.0. If it is unavailable or differs, run the release-pinned CLI through `npx --yes codex-sdlc@0.6.0`; do not require a global installation. Let the environment request approval if downloading the package requires network access. Infer the project name, application roots, technologies, and workspace shape from the selected repository. Ask only for missing information that changes repository topology or application ownership. State which checkout will own `.sdlc` before previewing a multi-repository installation. @@ -37,6 +37,8 @@ Report the selected shape, coordinator, mapped repositories, created files, runt ## Role models +For `/sdlc --save-my-token`, `/sdlc --normal`, or their `$sdlc` equivalents, follow the [sdlc entry point](../sdlc/SKILL.md). It applies a project preset through `configure-agents --save-my-token` or `configure-agents --normal`. These flags require runtime 0.6.0 or newer; upgrade older runtimes before applying a mode. Preserve optional PO settings and existing run snapshots. + When the user asks to choose agent models, use `configure-agents` after initialization or pass the same `--agent-model`, `--agent-reasoning`, `--agent-fallback`, and `--po-review` settings during `init`. Canonical roles are `pm`, `ba`, `backend`, `frontend`, `qc`, and `po`; translate FE/BE/Product Owner to `frontend`/`backend`/`po`. The frontend setting covers web and mobile. Use exact model IDs exposed by the current host and preserve the user’s selected models. Do not choose a model for an unspecified role. Example: `node .sdlc/runtime.cjs configure-agents --agent-model frontend=gpt-6-astra --agent-model backend=gpt-5.6-luna --agent-model qc=gpt-5.6-sol --agent-model pm=gpt-5.6-sol --dry-run`. Run it without `--dry-run` when the preview matches the authorized choices. `--po-review advisory` enables an inherited-model AI Product Owner; `--agent-model po=` also enables it. Final acceptance stays human. diff --git a/skills/sdlc/SKILL.md b/skills/sdlc/SKILL.md new file mode 100644 index 0000000..2496af0 --- /dev/null +++ b/skills/sdlc/SKILL.md @@ -0,0 +1,42 @@ +--- +name: sdlc +description: Handle /sdlc or $sdlc requests, including --save-my-token and --normal to persist delivery-role model presets for new runs in the selected project, or route a feature request to sdlc-pm. +--- + +# SDLC entry point + +Treat `/sdlc --save-my-token` and `/sdlc --normal` as chat shorthand for this skill. Explicit skill invocation is `$sdlc --save-my-token` or `$sdlc --normal`; this plugin does not register a native Codex slash command. If the client rejects `/sdlc` before sending the prompt, use the explicit skill form. + +## Project model mode + +1. Resolve the selected project's coordinator directory and read its `AGENTS.md` and `.sdlc/project.yaml`. In a multi-repository workspace, the coordinator owns this setting for all mapped applications. Never select a different project or write global Codex settings. If the coordinator is unknown, ask for its location. If the project is not initialized, explain that setup is required; use [sdlc-setup](../sdlc-setup/SKILL.md) when initialization is requested rather than inventing application roots. +2. Accept exactly one of `--save-my-token` and `--normal`. Both together, or either combined with custom role model, effort, fallback, reset, or PO settings, are conflicting requests: explain the conflict without changing configuration. Custom role changes can be made separately through [sdlc-setup](../sdlc-setup/SKILL.md). +3. From the coordinator, run the corresponding command below with `--dry-run --json` first. Inspect the returned settings. An explicit mode request authorizes saving that preset: unless the user asked only for a preview, run the same command without `--dry-run`. Do not ask for another confirmation for the requested mode. If the runtime rejects the new flag, report that its version lacks this feature and use the setup upgrade workflow with version 0.6.0 or newer. Do not rewrite project configuration manually or claim success after a failed command. + + ```sh + node .sdlc/runtime.cjs configure-agents --save-my-token --dry-run --json + node .sdlc/runtime.cjs configure-agents --save-my-token --json + ``` + + Or, for normal mode: + + ```sh + node .sdlc/runtime.cjs configure-agents --normal --dry-run --json + node .sdlc/runtime.cjs configure-agents --normal --json + ``` + +4. Report the saved project and role settings from the successful result. Settings apply to every new run in that project until changed. Existing runs keep their snapshots. A mode-only request ends here; it does not create or resume a delivery run. If the same request includes a new feature, save the mode before routing that feature to [sdlc-pm](../sdlc-pm/SKILL.md). Resuming a run keeps that run's original policy even after saving a mode. + +| Role | `--save-my-token` | `--normal` | +| --- | --- | --- | +| PM | Inherited | Inherited | +| BA | `gpt-6-sol`, `high` | Inherited | +| Backend | `gpt-6-luna`, `xhigh` | Inherited | +| Frontend (web and mobile) | `gpt-6-luna`, `xhigh` | Inherited | +| QC | `gpt-6-luna`, `xhigh` | Inherited | + +`xhigh` means extra-high reasoning. Both presets replace these five roles' previous models, efforts, and fallbacks. Normal mode restores inheritance rather than restoring earlier custom models. Both preserve optional AI Product Owner settings. No fallback is added. Dispatch still checks the host's model and reasoning capabilities; report an unavailable selection instead of silently substituting another model. The preset is a model configuration, not a guarantee about token usage. + +## Delivery requests + +For a feature request or explicit resume without either mode flag, use [sdlc-pm](../sdlc-pm/SKILL.md) and preserve the saved project mode. A bare `/sdlc` or `$sdlc` explains the two mode commands and asks what feature to start or run to resume; it does not reset models or start unspecified work. diff --git a/skills/sdlc/agents/openai.yaml b/skills/sdlc/agents/openai.yaml new file mode 100644 index 0000000..23e6f78 --- /dev/null +++ b/skills/sdlc/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "SDLC" + short_description: "Set project modes and start SDLC delivery" + default_prompt: "Use $sdlc to configure this project’s model mode or start a feature delivery." diff --git a/src/agents.ts b/src/agents.ts index 7e1e1f7..f83ca00 100644 --- a/src/agents.ts +++ b/src/agents.ts @@ -89,6 +89,8 @@ export function agentPolicyDiagnostics(value: unknown): string[] { export interface ConfigureAgentsOptions { root: string; + saveMyToken?: boolean; + normal?: boolean; models?: string[]; reasoning?: string[]; fallbacks?: string[]; @@ -99,7 +101,24 @@ export interface ConfigureAgentsOptions { /** A model replacement clears the old effort/fallbacks, which may be incompatible. */ export function updateAgentPolicy(current: AgentPolicy | undefined, options: Omit): AgentPolicy { + if (options.saveMyToken && options.normal) throw new Error("--save-my-token and --normal are mutually exclusive"); + const preset = options.saveMyToken || options.normal; + if (preset && ((options.models?.length ?? 0) + (options.reasoning?.length ?? 0) + + (options.fallbacks?.length ?? 0) + (options.resetRoles?.length ?? 0) > 0 || options.productOwnerReview !== undefined)) { + throw new Error("agent presets cannot be combined with --agent-model, --agent-reasoning, --agent-fallback, --reset-role, or --po-review"); + } const result: AgentPolicy = structuredClone(current ?? { roles: {}, product_owner_review: "disabled" }); + if (preset) { + for (const role of ["pm", "ba", "backend", "frontend", "qc"] as const) delete result.roles[role]; + if (options.saveMyToken) { + result.roles.ba = { model: "gpt-6-sol", reasoning_effort: "high" }; + for (const role of ["backend", "frontend", "qc"] as const) { + result.roles[role] = { model: "gpt-6-luna", reasoning_effort: "xhigh" }; + } + } + assertAgentPolicy(result); + return result; + } const resets = (options.resetRoles ?? []).map(parseRole); if (new Set(resets).size !== resets.length) throw new Error("duplicate reset role"); for (const role of resets) delete result.roles[role]; diff --git a/src/cli-grammar.ts b/src/cli-grammar.ts index 9fd080d..9cf2b2a 100644 --- a/src/cli-grammar.ts +++ b/src/cli-grammar.ts @@ -17,7 +17,7 @@ export const cliGrammar: Readonly> = { flagOptions: ["--redis", "--dry-run"], }, configure: { positionals: 0, requiredOptions: ["--repo"], optionalOptions: ["--root"], repeatableOptions: ["--repo"], flagOptions: ["--dry-run"] }, - "configure-agents": { positionals: 0, requiredOptions: [], optionalOptions: ["--root", "--po-review"], repeatableOptions: ["--agent-model", "--agent-reasoning", "--agent-fallback", "--reset-role"], flagOptions: ["--dry-run"] }, + "configure-agents": { positionals: 0, requiredOptions: [], optionalOptions: ["--root", "--po-review"], repeatableOptions: ["--agent-model", "--agent-reasoning", "--agent-fallback", "--reset-role"], flagOptions: ["--dry-run", "--save-my-token", "--normal"] }, "agent-plan": { positionals: 2, requiredOptions: [] }, "agent-dispatch": { positionals: 2, requiredOptions: [] }, doctor: { positionals: 0, requiredOptions: [], optionalOptions: ["--root"] }, @@ -92,5 +92,12 @@ export function validateCliArguments(arguments_: readonly string[]): CliGrammarD index += 2; } for (const option of required) if (!seen.has(option)) diagnostics.push({ message: `${commandName} missing required option ${option}` }); + if (commandName === "configure-agents") { + if (seen.has("--save-my-token") && seen.has("--normal")) diagnostics.push({ message: "--save-my-token and --normal are mutually exclusive" }); + if ((seen.has("--save-my-token") || seen.has("--normal")) + && ["--agent-model", "--agent-reasoning", "--agent-fallback", "--reset-role", "--po-review"].some((option) => seen.has(option))) { + diagnostics.push({ message: "agent presets cannot be combined with --agent-model, --agent-reasoning, --agent-fallback, --reset-role, or --po-review" }); + } + } return diagnostics; } diff --git a/src/cli.ts b/src/cli.ts index 64feaa6..6883f4b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -239,17 +239,19 @@ export async function main(rawArguments = process.argv.slice(2)): Promise", "coordinator repository root", ".") + .option("--save-my-token", "save project role models: BA Sol high, backend/frontend/QC Luna xhigh, PM inherited") + .option("--normal", "restore inherited models for PM, BA, backend, frontend, and QC") .option("--agent-model ", "set a role model; repeatable", collectOption, []) .option("--agent-reasoning ", "set a role reasoning effort; repeatable", collectOption, []) .option("--agent-fallback ", "set an explicit fallback, or role=none to clear it", collectOption, []) .option("--reset-role ", "restore a role to inherited model behavior; repeatable", collectOption, []) .option("--po-review ", "advisory or disabled") .option("--dry-run", "show settings without writing") - .action(async (options: AgentOptions & RootOptions & { dryRun?: boolean }) => { + .action(async (options: AgentOptions & RootOptions & { dryRun?: boolean; saveMyToken?: boolean; normal?: boolean }) => { executed = true; activeCommand = "configure-agents"; - if (!hasAgentOptions(options)) throw new CliFailure(exitCodes.usage, { code: "USAGE", message: "at least one agent setting is required" }); - const result = await configureAgents({ root: options.root ?? ".", ...agentOptions(options), dryRun: options.dryRun }); + if (!hasAgentOptions(options) && !options.saveMyToken && !options.normal) throw new CliFailure(exitCodes.usage, { code: "USAGE", message: "at least one agent setting is required" }); + const result = await configureAgents({ root: options.root ?? ".", ...agentOptions(options), saveMyToken: options.saveMyToken, normal: options.normal, dryRun: options.dryRun }); emit("configure-agents", result, `${result.dry_run ? "planned" : "saved"} role models in ${result.file}; applies to new runs`); }); diff --git a/src/constants.ts b/src/constants.ts index a69110a..edfc9e4 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,4 @@ export const FRAMEWORK_NAME = "codex-sdlc" as const; -export const FRAMEWORK_VERSION = "0.5.0" as const; +export const FRAMEWORK_VERSION = "0.6.0" as const; export const SCHEMA_VERSION = 1 as const; export const PROJECT_SCHEMA_VERSION = 2 as const; diff --git a/tests/agent-modes.test.ts b/tests/agent-modes.test.ts new file mode 100644 index 0000000..98641d7 --- /dev/null +++ b/tests/agent-modes.test.ts @@ -0,0 +1,162 @@ +import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { resolve } from "node:path"; + +import { afterEach, describe, expect, test, vi } from "vitest"; + +import { configureAgents, resolveAgentPlan, updateAgentPolicy, type AgentCapabilities, type AgentPolicy, type ConfigureAgentsOptions } from "../src/agents.js"; +import { main } from "../src/cli.js"; +import { validateCliArguments } from "../src/cli-grammar.js"; +import { loadProject } from "../src/config.js"; +import { initializeProject } from "../src/install.js"; +import { loadRun, startRun } from "../src/runs.js"; + +const roots: string[] = []; +const savedRoles: AgentPolicy["roles"] = { + ba: { model: "gpt-6-sol", reasoning_effort: "high" }, + backend: { model: "gpt-6-luna", reasoning_effort: "xhigh" }, + frontend: { model: "gpt-6-luna", reasoning_effort: "xhigh" }, + qc: { model: "gpt-6-luna", reasoning_effort: "xhigh" }, +}; +const capabilities: AgentCapabilities = { + source: "test host capability fixture; no live host dispatch", + model_selection: true, + reasoning_selection: true, + models: [ + { id: "gpt-6-sol", reasoning_efforts: ["high"] }, + { id: "gpt-6-luna", reasoning_efforts: ["xhigh"] }, + ], +}; + +afterEach(async () => { await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))); }); + +async function fixture(agents?: AgentPolicy): Promise { + const root = await mkdtemp(resolve(tmpdir(), "codex-sdlc-modes-")); + roots.push(root); + await Promise.all(["backend", "web", "mobile"].map((name) => mkdir(resolve(root, name)))); + await initializeProject({ root, projectName: "Modes", applications: ["backend", "web", "mobile"], backendRoot: "backend", webRoot: "web", mobileRoot: "mobile", agents, dryRun: false }); + await writeFile(resolve(root, ".sdlc/requests/request.md"), "Deliver the feature across backend, web, and mobile.\n"); + return root; +} + +async function run(root: string, id: string) { + await startRun(root, { id, title: "Model preset", requestFile: ".sdlc/requests/request.md", affectedApplications: { backend: true, web: true, mobile: true, database: false, sharedPackages: false }, now: "2026-09-24T08:00:00.000Z" }); + return loadRun(root, id); +} + +async function cli(root: string, ...options: string[]) { + const chunks: string[] = []; + const previousExitCode = process.exitCode; + const output = vi.spyOn(process.stdout, "write").mockImplementation((chunk) => { chunks.push(String(chunk)); return true; }); + try { + const code = await main(["configure-agents", "--root", root, ...options, "--json"]); + return { code, payload: JSON.parse(chunks.join("")) }; + } finally { + output.mockRestore(); + process.exitCode = previousExitCode; + } +} + +describe("project model presets", () => { + test("CLI saves both presets idempotently, clearing old settings while preserving PO and project content", async () => { + const original = updateAgentPolicy(undefined, { + models: ["pm=gpt-6-astra", "ba=gpt-6-astra", "backend=gpt-6-astra", "frontend=gpt-6-astra", "qc=gpt-6-astra", "po=gpt-6-astra"], + reasoning: ["pm=ultra", "ba=ultra", "backend=ultra", "frontend=ultra", "qc=ultra", "po=ultra"], + fallbacks: ["pm=gpt-6-sol:high", "ba=gpt-6-sol:high", "backend=gpt-6-sol:high", "frontend=gpt-6-sol:high", "qc=gpt-6-sol:high", "po=gpt-6-sol:high"], + }); + const root = await fixture(original); + const otherRoot = await fixture(); + const otherBefore = await readFile(resolve(otherRoot, ".sdlc/project.yaml"), "utf8"); + const path = resolve(root, ".sdlc/project.yaml"); + await writeFile(path, `# Keep the project comment\n${await readFile(path, "utf8")}`); + const projectBefore = await loadProject(root); + for (const flag of ["--save-my-token", "--normal"]) { + const expected: AgentPolicy = { roles: { ...(flag === "--save-my-token" ? savedRoles : {}), po: original.roles.po }, product_owner_review: "advisory" }; + const result = await cli(root, flag); + expect(result.code).toBe(0); + expect(result.payload).toMatchObject({ ok: true, command: "configure-agents", result: { agents: expected, applies_to: "new runs", dry_run: false } }); + expect(await loadProject(root)).toEqual({ ...projectBefore, agents: expected }); + const saved = await readFile(path, "utf8"); + expect(saved).toContain("# Keep the project comment"); + expect((await cli(root, flag)).code).toBe(0); + expect(await readFile(path, "utf8")).toBe(saved); + } + expect(await readFile(resolve(otherRoot, ".sdlc/project.yaml"), "utf8")).toBe(otherBefore); + expect(original.roles.pm?.reasoning_effort).toBe("ultra"); + }); + + test.each(["--save-my-token", "--normal"])("CLI previews %s without writing", async (flag) => { + const root = await fixture(updateAgentPolicy(undefined, { models: ["pm=gpt-6-astra"] })); + const path = resolve(root, ".sdlc/project.yaml"); + const before = await readFile(path, "utf8"); + const result = await cli(root, flag, "--dry-run"); + expect(result.code).toBe(0); + expect(result.payload.result).toMatchObject({ dry_run: true, agents: { roles: flag === "--save-my-token" ? savedRoles : {}, product_owner_review: "disabled" } }); + expect(await readFile(path, "utf8")).toBe(before); + }); + + test("new runs snapshot the project setting and existing legacy/saving runs remain unchanged", async () => { + const root = await fixture(); + const legacy = await run(root, "LEGACY-001"); + expect(legacy.agent_policy).toBeUndefined(); + const legacyPath = resolve(root, ".sdlc/runs/LEGACY-001/manifest.yaml"); + const legacyBefore = await readFile(legacyPath, "utf8"); + expect((await cli(root, "--save-my-token")).code).toBe(0); + const saving = await run(root, "SAVING-001"); + expect(saving.agent_policy).toEqual({ roles: savedRoles, product_owner_review: "disabled" }); + expect((await run(root, "SAVING-002")).agent_policy).toEqual(saving.agent_policy); + const savingPath = resolve(root, ".sdlc/runs/SAVING-001/manifest.yaml"); + const savingBefore = await readFile(savingPath, "utf8"); + expect((await cli(root, "--normal")).code).toBe(0); + const normal = await run(root, "NORMAL-001"); + expect(normal.agent_policy).toEqual({ roles: {}, product_owner_review: "disabled" }); + expect(await readFile(legacyPath, "utf8")).toBe(legacyBefore); + expect(await readFile(savingPath, "utf8")).toBe(savingBefore); + for (const taskId of ["PM-001", "BA-001", "BE-001", "BE-002", "WEB-001", "MOB-001", "QC-001"]) { + expect(resolveAgentPlan(normal, taskId, capabilities).selected).toBeNull(); + const plan = resolveAgentPlan(saving, taskId, capabilities); + expect(plan.selected).toEqual(taskId.startsWith("PM") ? null : taskId.startsWith("BA") ? savedRoles.ba : savedRoles.backend); + expect(plan.requested).toEqual(plan.selected); + expect(plan.fallback_used).toBe(false); + } + expect(() => resolveAgentPlan(saving, "MOB-001", { ...capabilities, models: [] })).toThrow("No implicit fallback"); + expect(() => resolveAgentPlan(saving, "QC-001", { ...capabilities, reasoning_selection: false })).toThrow("No implicit fallback"); + }); + + test("normal works on a legacy project without a policy", async () => { + const root = await fixture(); + expect((await cli(root, "--normal")).code).toBe(0); + expect((await loadProject(root)).agents).toEqual({ roles: {}, product_owner_review: "disabled" }); + }); + + test("grammar and CLI reject conflicting presets and manual settings without writing", async () => { + const root = await fixture(); + const path = resolve(root, ".sdlc/project.yaml"); + const before = await readFile(path, "utf8"); + const manual = [["--agent-model", "ba=gpt-6-sol"], ["--agent-reasoning", "ba=high"], ["--agent-fallback", "ba=gpt-6-sol:high"], ["--reset-role", "pm"], ["--po-review", "advisory"]]; + for (const options of [["--save-my-token", "--normal"], ...["--save-my-token", "--normal"].flatMap((preset) => manual.map((settings) => [preset, ...settings]))]) { + expect(validateCliArguments(["configure-agents", ...options]).length).toBeGreaterThan(0); + const result = await cli(root, ...options); + expect(result.code).not.toBe(0); + expect(result.payload).toMatchObject({ ok: false, diagnostics: [{ code: "USAGE" }] }); + expect(await readFile(path, "utf8")).toBe(before); + } + for (const flag of ["--save-my-token", "--normal"]) { + expect(validateCliArguments(["configure-agents", flag, "--dry-run", "--root", root, "--json"])).toEqual([]); + expect(validateCliArguments(["init", "--name", "Example", flag]).length).toBeGreaterThan(0); + expect(validateCliArguments(["configure-agents", flag, "unexpected-value"]).length).toBeGreaterThan(0); + } + }); + + test("programmatic configuration rejects conflicts before writing", async () => { + const root = await fixture(); + const path = resolve(root, ".sdlc/project.yaml"); + const before = await readFile(path, "utf8"); + const manual: Partial[] = [{ models: ["ba=gpt-6-sol"] }, { reasoning: ["ba=high"] }, { fallbacks: ["ba=gpt-6-sol:high"] }, { resetRoles: ["pm"] }, { productOwnerReview: "advisory" }]; + for (const options of [{ saveMyToken: true, normal: true }, ...[{ saveMyToken: true }, { normal: true }].flatMap((preset) => manual.map((settings) => ({ ...preset, ...settings })))]) { + expect(() => updateAgentPolicy(undefined, options)).toThrow(/mutually exclusive|cannot be combined/); + await expect(configureAgents({ ...options, root })).rejects.toThrow(/mutually exclusive|cannot be combined/); + expect(await readFile(path, "utf8")).toBe(before); + } + }); +}); diff --git a/tests/package-content.test.ts b/tests/package-content.test.ts index 50874e8..96a17eb 100644 --- a/tests/package-content.test.ts +++ b/tests/package-content.test.ts @@ -31,7 +31,7 @@ describe("distribution content", () => { const manifest = JSON.parse(await readFile(resolve(root, "plugin.json"), "utf8")); expect(manifest.$schema).toBe("https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"); expect(manifest.name).toBe("codex-sdlc"); - expect(manifest.version).toBe("0.5.0"); + expect(manifest.version).toBe("0.6.0"); expect(manifest.license).toBe("Apache-2.0"); expect(manifest.extensions["com.openai"].interface.capabilities).toEqual(["Read", "Write"]); expect(manifest.extensions["com.openai"].interface.logo).toBe("./assets/brand/codex-sdlc.svg"); @@ -40,7 +40,7 @@ describe("distribution content", () => { expect(manifest.extensions["com.openai"].interface.defaultPrompt.every((prompt: string) => prompt.length <= 128)).toBe(true); expect(manifest.extensions["com.openai"].interface.websiteURL).toBe("https://github.com/prime-vimihi/codex-sdlc/blob/main/docs/getting-started.md"); const codexManifest = JSON.parse(await readFile(resolve(root, ".codex-plugin/plugin.json"), "utf8")); - expect(codexManifest.version).toBe("0.5.0"); + expect(codexManifest.version).toBe("0.6.0"); expect(codexManifest.interface.defaultPrompt.length).toBeLessThanOrEqual(3); expect(codexManifest.interface.defaultPrompt.every((prompt: string) => prompt.length <= 128)).toBe(true); const packageDocument = JSON.parse(await readFile(resolve(root, "package.json"), "utf8"));