From bcd0af3d91b27d6b44ecc089a8523400ed975012 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Sat, 15 Aug 2026 10:18:53 -0700 Subject: [PATCH] docs(openspec): make every spec pass validate --all --strict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two specs failed. Fixing them surfaced a third problem that was not failing, which is the worse one. `cli-update-engine` is deleted. It was a tombstone: the capability was decommissioned, every requirement removed, and the file kept "for historical reference". specs/ describes what is true now, and the history is already in the 2026-03-30-remove-scaffold-dependency archive, including that change's own cli-update-engine delta. Nothing outside the archives referenced it. `cli-rules` gets scenarios and SHALL keywords on 15 requirements, and loses the five that specify `taskless rule verify` — a command this stack removed and replaced with the path-addressed `verify` and `test`, now owned by cli-rule-validation. A requirement stating the subcommand is gone replaces them. Two requirements also named pre-layout paths (`.taskless/rule-tests/.yml`, and deleting "rule and test files" rather than the rule directory); both repathed while being touched. The third problem: a second `##` inside a requirements section ends it, so every requirement below stops being read. `cli-rules` hid 10 behind `## API Contract`, `infrastructure` hid 19, and `skills` hid 6. Those last two PASSED --strict throughout, because the single requirement each still exposed was well-formed. 35 requirements were unread while the validator reported success. The groupings are preserved as bold lead-in lines instead, and un-nesting infrastructure immediately exposed a real requirement with no SHALL, now fixed. `skills` also still told agents to fetch recipes via `npx @taskless/cli help `. 23 specs, 0 failures. Refs #105 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Jwc9FFroR3mTZ4hLiSkkX3 --- openspec/specs/cli-rules/spec.md | 146 +++++++++++++++++------ openspec/specs/cli-update-engine/spec.md | 9 -- openspec/specs/infrastructure/spec.md | 14 ++- openspec/specs/skills/spec.md | 8 +- 4 files changed, 120 insertions(+), 57 deletions(-) delete mode 100644 openspec/specs/cli-update-engine/spec.md diff --git a/openspec/specs/cli-rules/spec.md b/openspec/specs/cli-rules/spec.md index e6794a6b..ebd41f0b 100644 --- a/openspec/specs/cli-rules/spec.md +++ b/openspec/specs/cli-rules/spec.md @@ -34,7 +34,13 @@ The `taskless rule create` command SHALL accept a `--from ` flag specifyin ### Requirement: Rules create resolves identity from JWT and git remote -`taskless rule create` resolves user identity from the stored JWT and the git remote per the existing identity resolution requirements. (Renamed to singular.) +`taskless rule create` SHALL resolve user identity from the stored JWT and the git remote per the existing identity resolution requirements. (Renamed to singular.) + +#### Scenario: Identity comes from the token and the remote + +- **WHEN** an authenticated user runs `taskless rule create` +- **THEN** the CLI SHALL take the organization from the stored JWT +- **AND** it SHALL take the repository from the git remote rather than prompting for either ### Requirement: Rules create requires authentication @@ -52,7 +58,13 @@ The `taskless rule create` command SHALL accept a `--from ` flag specifyin ### Requirement: Rules create submits to API and polls for results -`taskless rule create` (without `--anonymous`) submits to the API and polls per the existing requirement. (Renamed to singular.) +`taskless rule create` without `--anonymous` SHALL submit the request to the API and poll for the result per the existing requirement. (Renamed to singular.) + +#### Scenario: Submission returns a request to poll + +- **WHEN** an authenticated user runs `taskless rule create` without `--anonymous` +- **THEN** the CLI SHALL submit the request to the API +- **AND** it SHALL poll for the result until the generation completes or fails ### Requirement: Rules create uses a network interface with stub @@ -79,51 +91,119 @@ The API calls for rule generation (`POST /cli/api/request` and `GET /cli/api/req ### Requirement: Rules create writes test files to disk -`taskless rule create` SHALL write generated test files to `.taskless/rule-tests/.yml` regardless of whether `--anonymous` was set. (Renamed; strengthened.) +`taskless rule create` SHALL write generated test files into the rule's own directory, at `.taskless/rules/sg//.tests/`, regardless of whether `--anonymous` was set. (Renamed; strengthened; repathed for the rule-directory layout.) + +#### Scenario: Tests land inside the rule they cover + +- **WHEN** `taskless rule create` generates test cases for rule `` +- **THEN** the CLI SHALL write them under `.taskless/rules/sg//.tests/` +- **AND** it SHALL do so whether or not `--anonymous` was set ### Requirement: Rules create outputs results -`taskless rule create` outputs results per the existing requirement. (Renamed to singular.) Output SHALL be human-readable by default; `--json` produces machine-readable output. On failure with `--json` set, the output SHALL be the standardized error envelope `{ ok: false, code: "", message: "<...>" }` per the `cli` capability requirements. +`taskless rule create` SHALL output results per the existing requirement. (Renamed to singular.) Output SHALL be human-readable by default; `--json` produces machine-readable output. On failure with `--json` set, the output SHALL be the standardized error envelope `{ ok: false, code: "", message: "<...>" }` per the `cli` capability requirements. + +#### Scenario: Failure under --json uses the error envelope + +- **WHEN** `taskless rule create --json` fails +- **THEN** the CLI SHALL print `{ ok: false, code, message }` rather than prose ### Requirement: Rules create shows progress during polling -`taskless rule create` shows progress per the existing requirement when polling the API (the `--anonymous` branch does not poll an API and SHOULD show progress for the local agent-driven steps if applicable). (Renamed to singular.) +`taskless rule create` SHALL show progress while polling the API. The `--anonymous` branch polls nothing and SHOULD show progress for the local agent-driven steps where applicable. (Renamed to singular.) + +#### Scenario: Polling reports progress + +- **WHEN** `taskless rule create` is waiting on the API +- **THEN** the CLI SHALL report progress rather than appearing to hang ### Requirement: Rules improve reads request from file `taskless rule improve` SHALL accept a `--from ` flag specifying a JSON file containing the iterate request. (Renamed to singular.) +#### Scenario: The request is read from the named file + +- **WHEN** a user runs `taskless rule improve --from request.json` +- **THEN** the CLI SHALL read the iterate request from that file + ### Requirement: Rules improve requires authentication `taskless rule improve` SHALL require authentication unless `--anonymous` is set. (Renamed; new anonymous branch.) +#### Scenario: Authentication is required without --anonymous + +- **WHEN** a logged-out user runs `taskless rule improve` without `--anonymous` +- **THEN** the CLI SHALL exit non-zero and direct the user to authenticate + +#### Scenario: The anonymous branch skips authentication + +- **WHEN** a logged-out user runs `taskless rule improve --anonymous` +- **THEN** the CLI SHALL run the local-only flow without requiring a login + ### Requirement: Rules improve submits to iterate API and polls for results -`taskless rule improve` (without `--anonymous`) submits and polls per the existing requirement. (Renamed.) +`taskless rule improve` without `--anonymous` SHALL submit to the iterate API and poll for the result per the existing requirement. (Renamed.) + +#### Scenario: Submission returns a request to poll + +- **WHEN** an authenticated user runs `taskless rule improve` without `--anonymous` +- **THEN** the CLI SHALL submit to the iterate API +- **AND** it SHALL poll until the iteration completes or fails ### Requirement: Rules improve writes updated files to disk `taskless rule improve` SHALL write updated rule files to disk in both branches. (Renamed; strengthened.) +#### Scenario: Both branches persist the updated rule + +- **WHEN** `taskless rule improve` completes, with or without `--anonymous` +- **THEN** the CLI SHALL write the updated rule to its canonical location on disk + ### Requirement: Rules improve outputs results -`taskless rule improve` outputs results per the existing requirement. (Renamed.) Failure output with `--json` SHALL use the standardized error envelope. +`taskless rule improve` SHALL output results per the existing requirement. (Renamed.) Failure output with `--json` SHALL use the standardized error envelope. + +#### Scenario: Failure under --json uses the error envelope + +- **WHEN** `taskless rule improve --json` fails +- **THEN** the CLI SHALL print `{ ok: false, code, message }` + +### Requirement: Rules improve has an agent recipe -### Requirement: Rules improve has a help entry +`taskless agent improve-rule` SHALL return the recipe per `cli-help` requirements. The recipe file is `improve-rule.txt`, with an `improve-rule.anonymous.txt` variant for the local-only flow. -`taskless help rule improve` SHALL return the recipe per `cli-help` requirements. (Renamed; the help filename becomes `rule-improve.txt` with an optional `rule-improve.anonymous.txt` variant.) +#### Scenario: The recipe resolves by its single-token name + +- **WHEN** a user runs `taskless agent improve-rule` +- **THEN** the CLI SHALL print the contents of `improve-rule.txt` ### Requirement: Rules delete removes rule and test files -`taskless rule delete ` SHALL remove the corresponding rule file and any test files. (Renamed.) Accepts `--anonymous` as a no-op. +`taskless rule delete ` SHALL remove the rule and everything that defines it. Under the rule-directory layout that is one directory, `.taskless/rules///`, which carries the rule, any per-engine config, and its tests. (Renamed; repathed.) Accepts `--anonymous` as a no-op. + +#### Scenario: Deleting a rule removes its whole directory + +- **WHEN** a user runs `taskless rule delete no-eval` +- **THEN** the CLI SHALL remove the rule's directory including its `.tests/` +- **AND** no file belonging to that rule SHALL remain ### Requirement: Rules delete does not require authentication -`taskless rule delete` does not require authentication per the existing requirement. (Renamed.) +`taskless rule delete` SHALL NOT require authentication. Deleting a local file is not a service operation. (Renamed.) + +#### Scenario: Deletion works logged out + +- **WHEN** a logged-out user runs `taskless rule delete no-eval` +- **THEN** the CLI SHALL delete the rule without requiring a login ### Requirement: Rules delete accepts the id argument -`taskless rule delete ` accepts the rule ID as a positional argument per the existing requirement. (Renamed.) +`taskless rule delete ` SHALL accept the rule ID as a positional argument per the existing requirement. (Renamed.) + +#### Scenario: The id is positional + +- **WHEN** a user runs `taskless rule delete no-eval` +- **THEN** the CLI SHALL treat `no-eval` as the rule ID ### Requirement: Codegen script fetches official ast-grep rule schema @@ -160,6 +240,16 @@ The codegen script SHALL extract the ast-grep version from `packages/cli/package - **THEN** the codegen script SHALL exit with a non-zero code and a descriptive error message - **AND** SHALL NOT overwrite an existing generated schema file +### Requirement: The rule subcommand group no longer validates rules + +`taskless rule verify` SHALL NOT exist. Rule validation is addressed by path through the top-level `verify` and `test` commands, specified by the `cli-rule-validation` capability. + +#### Scenario: The removed subcommand does not resolve + +- **WHEN** a user runs `taskless rule verify no-eval` +- **THEN** the CLI SHALL exit non-zero +- **AND** it SHALL NOT validate a rule + ### Requirement: Generated schema is importable at build time The generated JSON Schema file SHALL be importable by the CLI bundle via Vite. The import SHALL make the full JSON Schema object available at runtime without filesystem reads or network fetches. @@ -170,31 +260,6 @@ The generated JSON Schema file SHALL be importable by the CLI bundle via Vite. T - **THEN** it SHALL import the schema from `../generated/ast-grep-rule-schema.json` - **AND** the schema object SHALL be available synchronously at runtime -### Requirement: Verify subcommand validates rules against ast-grep schema - -`taskless rule verify` SHALL validate rules against the ast-grep schema per the existing requirement. (Renamed from `rules verify` to `rule verify`.) Accepts `--anonymous` as a no-op. - -### Requirement: Verify performs three layers of validation - -`taskless rule verify` performs the three layers of validation per the existing requirement. (Renamed.) - -### Requirement: Verify supports JSON output - -`taskless rule verify --json` outputs results in the documented JSON shape. On failure, the standardized error envelope is used. (Renamed.) - -### Requirement: Verify schema mode dumps combined schema for agent consumption - -The `taskless rule verify --schema` mode is REMOVED in v0.7.0 — schemas are now embedded in `tskl help rule create` recipe output via `zod-to-json-schema`. (Renamed and superseded.) - -#### Scenario: --schema flag is no longer accepted - -- **WHEN** a user runs `taskless rule verify --schema` -- **THEN** the CLI SHALL exit with an error indicating the flag is unknown - -### Requirement: Verify respects global flags - -`taskless rule verify` respects global flags including `--dir` per the existing requirement. (Renamed.) Also accepts the new `--anonymous` flag as a no-op. - ### Requirement: Rule create supports anonymous local-only flow When `taskless rule create --anonymous` is invoked, the CLI SHALL execute the local-only rule-creation flow (previously implemented as the `taskless-create-rule-anonymous` skill body). The flow SHALL: @@ -232,7 +297,10 @@ When `taskless rule improve --anonymous` is invoked, the CLI SHALL execute the l - **THEN** the CLI SHALL NOT make any HTTP request to the Taskless API - **AND** SHALL update the target rule file -## API Contract +**API contract.** The requirements below describe the service endpoints the +`rule` subcommands call. They are grouped by a bold line rather than a +heading: a second `##` inside this section ends it, and everything after it +stops being read as a requirement. ### Requirement: Rule generation request endpoint accepts a request and returns a requestId @@ -352,7 +420,7 @@ Each rule in the `rules` array SHALL contain an `id` (string), a `content` objec ### Requirement: Generated rules may include test cases -Each rule in the `rules` array MAY include a `tests` object containing `valid` (array of strings — code that should NOT trigger the rule) and `invalid` (array of strings — code that SHOULD trigger the rule). +Each rule in the `rules` array MAY include a `tests` object. When present it SHALL contain `valid` (array of strings, code that must not trigger the rule) and `invalid` (array of strings, code that must trigger it). #### Scenario: Rule with test cases diff --git a/openspec/specs/cli-update-engine/spec.md b/openspec/specs/cli-update-engine/spec.md deleted file mode 100644 index 1ce0f1ab..00000000 --- a/openspec/specs/cli-update-engine/spec.md +++ /dev/null @@ -1,9 +0,0 @@ -# CLI Update Engine - -## Purpose - -This capability has been removed. The `update-engine` subcommand and all associated backend endpoints have been decommissioned. The CLI no longer manages scaffold upgrades. - -## Requirements - -_All requirements have been removed. This spec is retained for historical reference._ diff --git a/openspec/specs/infrastructure/spec.md b/openspec/specs/infrastructure/spec.md index 0a49ce8b..0c3da89d 100644 --- a/openspec/specs/infrastructure/spec.md +++ b/openspec/specs/infrastructure/spec.md @@ -4,7 +4,11 @@ Defines build tooling, CI pipelines, and repository configuration including version sync, command generation, Turborepo setup, and GitHub Actions workflows. -## Build Tooling +## Requirements + +**Build tooling.** Grouped by a bold line rather than a heading: a second `##` +inside the requirements section ends it, and every requirement after it +stops being read. ### Requirement: tsx is available for build scripts @@ -41,7 +45,7 @@ A `scripts/sync-skill-versions.ts` script SHALL read the version from `packages/ ### Requirement: Slash command files are hand-authored -Since the v0.7 consolidation, the single `commands/tskl/tskl.md` slash command is hand-authored rather than generated from a `SKILL.md` body. The command body intentionally differs from the skill body (it is a `$ARGUMENTS`-aware router), so the prior "copy SKILL.md body to command" generation script no longer applies. +The single `commands/tskl/tskl.md` slash command SHALL be hand-authored rather than generated from a `SKILL.md` body. Its body intentionally differs from the skill body (it is a `$ARGUMENTS`-aware router), so the prior "copy SKILL.md body to command" generation script SHALL NOT be reintroduced. #### Scenario: Single hand-authored command file exists @@ -84,7 +88,7 @@ The `packages/cli/package.json` SHALL NOT have a `release` script. Build and pub - **WHEN** inspecting `packages/cli/package.json` scripts - **THEN** there SHALL be no `release` key -## Repository Configuration +**Repository configuration.** ### Requirement: Turborepo is configured at the repo root @@ -134,7 +138,7 @@ The root `pnpm typecheck` command SHALL invoke `turbo run typecheck`, which runs - **WHEN** `pnpm typecheck` is run at the repo root - **THEN** Turborepo SHALL execute `typecheck` in `@taskless/cli` -## Continuous Integration +**Continuous integration.** ### Requirement: CI workflow exists @@ -247,7 +251,7 @@ The workflow SHALL NOT include any publish, release, or npm registry push steps. - **WHEN** inspecting the workflow file - **THEN** there SHALL be no steps that run `pnpm publish`, `npm publish`, or interact with an npm registry -## Requirements +**Release and publishing.** ### Requirement: Script-versioned packages are excluded from changesets diff --git a/openspec/specs/skills/spec.md b/openspec/specs/skills/spec.md index 0b8dc956..894ae640 100644 --- a/openspec/specs/skills/spec.md +++ b/openspec/specs/skills/spec.md @@ -10,7 +10,7 @@ Defines the structure, conventions, and distribution model for Taskless skills, The single skill SHALL be defined at `skills/taskless/SKILL.md` with YAML frontmatter (`name`, `description`, `metadata`) followed by markdown instructions. The `name` field SHALL be exactly `taskless` (no per-task prefix). The `metadata` field SHALL include `author`, `version`, and `commandName: tskl` keys. The `version` SHALL be used for staleness detection when the skill is installed into target repositories. -The skill body SHALL begin by instructing the agent that it does NOT have step-by-step instructions for any Taskless action and that recipes must be fetched via `npx @taskless/cli help ` before proceeding. The body SHALL NOT contain inline step-by-step recipes for any individual task — those live in `packages/cli/src/help/.txt` files served by the help subcommand. +The skill body SHALL begin by instructing the agent that it does NOT have step-by-step instructions for any Taskless action and that recipes must be fetched via `npx @taskless/cli agent ` before proceeding. The body SHALL NOT contain inline step-by-step recipes for any individual task — those live in `packages/cli/src/help/.txt` files served by the help subcommand. #### Scenario: Skill directory contains valid SKILL.md @@ -23,10 +23,10 @@ The skill body SHALL begin by instructing the agent that it does NOT have step-b #### Scenario: Skill body delegates to CLI help - **WHEN** the skill body is read -- **THEN** it SHALL instruct the agent to fetch the canonical recipe via `npx @taskless/cli help ` before performing any Taskless action +- **THEN** it SHALL instruct the agent to fetch the canonical recipe via `npx @taskless/cli agent ` before performing any Taskless action - **AND** SHALL NOT duplicate recipe content inline -## Distribution +**Distribution.** ### Requirement: Skills live in the standard discovery path @@ -59,7 +59,7 @@ The single skill name SHALL be `taskless` (without a per-task suffix). When inst ### Requirement: Commands directory contains Claude Code command files -The `commands/tskl/` directory SHALL contain exactly one command file (`tskl.md`) that maps to the consolidated skill. The command body SHALL accept a free-form `$ARGUMENTS` ask and route via the same flow as the skill (fetch `npx @taskless/cli help `, follow the recipe). When `$ARGUMENTS` is empty or ambiguous, the command body SHALL instruct the agent to ask the user what they want to do. +The `commands/tskl/` directory SHALL contain exactly one command file (`tskl.md`) that maps to the consolidated skill. The command body SHALL accept a free-form `$ARGUMENTS` ask and route via the same flow as the skill (fetch `npx @taskless/cli agent `, follow the recipe). When `$ARGUMENTS` is empty or ambiguous, the command body SHALL instruct the agent to ask the user what they want to do. #### Scenario: Single command file exists