From 06821333fde10e82e922ebdec5777d574142a6e1 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Tue, 1 Sep 2026 20:39:55 -0700 Subject: [PATCH 1/2] chore(openspec): archive generator-payload-alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last open task was waiting on the generator sending `ruleId` on a reconcile entry, which landed in #242: `unsafe`, `unknown` and `missing` all carry it, and `ruleIdFromCheckPath` is deleted. N4 is confirmed both sides — the engine tier stays defined and unused, since G2 means a Vale rule cannot be delivered without its `.vale.ini` and so the middle rung has no future occupant. The directory move is the smaller half. Archiving promotes the change's spec deltas into the standing specs, which is what makes the contract official: `cli-generated-rule-delivery` and `cli-layout-export` become capabilities, and `cli-rule-reconciliation`, `cli-rules` and `cli-runtime-rule-execution` take the requirements the slices implemented. Eight requirements added, two modified. No code changes. The changeset was grown as the stack drained and already carries every slice. --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/cli-generated-rule-delivery/spec.md | 0 .../specs/cli-layout-export/spec.md | 0 .../specs/cli-rule-reconciliation/spec.md | 0 .../specs/cli-rules/spec.md | 0 .../specs/cli-runtime-rule-execution/spec.md | 0 .../tasks.md | 8 +- .../specs/cli-generated-rule-delivery/spec.md | 48 ++++++++++ openspec/specs/cli-layout-export/spec.md | 35 ++++++++ .../specs/cli-rule-reconciliation/spec.md | 40 +++++++++ openspec/specs/cli-rules/spec.md | 14 +++ .../specs/cli-runtime-rule-execution/spec.md | 89 ++++++++++++++++--- 14 files changed, 219 insertions(+), 15 deletions(-) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/.openspec.yaml (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/design.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/proposal.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/specs/cli-generated-rule-delivery/spec.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/specs/cli-layout-export/spec.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/specs/cli-rule-reconciliation/spec.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/specs/cli-rules/spec.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/specs/cli-runtime-rule-execution/spec.md (100%) rename openspec/changes/{generator-payload-alignment => archive/2026-09-02-generator-payload-alignment}/tasks.md (85%) create mode 100644 openspec/specs/cli-generated-rule-delivery/spec.md create mode 100644 openspec/specs/cli-layout-export/spec.md diff --git a/openspec/changes/generator-payload-alignment/.openspec.yaml b/openspec/changes/archive/2026-09-02-generator-payload-alignment/.openspec.yaml similarity index 100% rename from openspec/changes/generator-payload-alignment/.openspec.yaml rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/.openspec.yaml diff --git a/openspec/changes/generator-payload-alignment/design.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/design.md similarity index 100% rename from openspec/changes/generator-payload-alignment/design.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/design.md diff --git a/openspec/changes/generator-payload-alignment/proposal.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/proposal.md similarity index 100% rename from openspec/changes/generator-payload-alignment/proposal.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/proposal.md diff --git a/openspec/changes/generator-payload-alignment/specs/cli-generated-rule-delivery/spec.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-generated-rule-delivery/spec.md similarity index 100% rename from openspec/changes/generator-payload-alignment/specs/cli-generated-rule-delivery/spec.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-generated-rule-delivery/spec.md diff --git a/openspec/changes/generator-payload-alignment/specs/cli-layout-export/spec.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-layout-export/spec.md similarity index 100% rename from openspec/changes/generator-payload-alignment/specs/cli-layout-export/spec.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-layout-export/spec.md diff --git a/openspec/changes/generator-payload-alignment/specs/cli-rule-reconciliation/spec.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-rule-reconciliation/spec.md similarity index 100% rename from openspec/changes/generator-payload-alignment/specs/cli-rule-reconciliation/spec.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-rule-reconciliation/spec.md diff --git a/openspec/changes/generator-payload-alignment/specs/cli-rules/spec.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-rules/spec.md similarity index 100% rename from openspec/changes/generator-payload-alignment/specs/cli-rules/spec.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-rules/spec.md diff --git a/openspec/changes/generator-payload-alignment/specs/cli-runtime-rule-execution/spec.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-runtime-rule-execution/spec.md similarity index 100% rename from openspec/changes/generator-payload-alignment/specs/cli-runtime-rule-execution/spec.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/specs/cli-runtime-rule-execution/spec.md diff --git a/openspec/changes/generator-payload-alignment/tasks.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md similarity index 85% rename from openspec/changes/generator-payload-alignment/tasks.md rename to openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md index e7e26011..af23ea30 100644 --- a/openspec/changes/generator-payload-alignment/tasks.md +++ b/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md @@ -57,10 +57,10 @@ green on its own; none depends on a later one to be correct. - [x] 6.5 Nothing repaired runs in the pass that repaired it. Restore rewrites the working tree and promotes nothing into the current run: an `unsafe` rule stays withheld, a `missing` rule was never a local candidate, and an `unknown` rule never runs. Fetching code and executing it in the same pass that discovered the drift would move the gate - [x] 6.6 A repair that fails is a notice, never a failed `check`. A rule that could not be repaired stays withheld, which is already the safe state -- [ ] 6.7 Ask the generator for `ruleId` on a reconcile `unsafe` entry (**N6**). Until then the id is parsed out of `.taskless/rules/runtime//check.ts`, which works and makes repair depend on a layout that has already moved twice — silently, since a wrong id is a 404 and an unrepaired rule rather than an error +- [x] 6.7 Ask the generator for `ruleId` on a reconcile `unsafe` entry (**N6**). **Done**: `unsafe`, `unknown` and `missing` entries all carry `ruleId`, and `ruleIdFromCheckPath` is deleted. An entry arriving without a usable id is skipped with a notice rather than becoming a request for `undefined`. Until then the id is parsed out of `.taskless/rules/runtime//check.ts`, which works and makes repair depend on a layout that has already moved twice — silently, since a wrong id is a 404 and an unrepaired rule rather than an error ## 7. Close out -- [ ] 7.1 Reply to **N4**: leave the `engine` tier defined and unused — G2 means no engine but `sg` is deliverable as a single file, so the middle rung has no future occupant either -- [ ] 7.2 Grow the changeset as each slice lands; it stays on the bottom branch -- [ ] 7.3 Archive the change on the final slice +- [x] 7.1 Reply to **N4**: **Done**, confirmed both sides — leave the `engine` tier defined and unused — G2 means no engine but `sg` is deliverable as a single file, so the middle rung has no future occupant either +- [x] 7.2 Grow the changeset as each slice lands; it stays on the bottom branch +- [x] 7.3 Archive the change on the final slice diff --git a/openspec/specs/cli-generated-rule-delivery/spec.md b/openspec/specs/cli-generated-rule-delivery/spec.md new file mode 100644 index 00000000..7faf4d73 --- /dev/null +++ b/openspec/specs/cli-generated-rule-delivery/spec.md @@ -0,0 +1,48 @@ +# cli-generated-rule-delivery Specification + +## Purpose + +TBD - created by archiving change generator-payload-alignment. Update Purpose after archive. + +## Requirements + +### Requirement: A delivered rule is a file set + +The CLI SHALL accept a generated rule as a set of files, each with a path relative to +`.taskless/rules///` and its content as text. One shape SHALL serve every engine, +validated against `ENGINE_LAYOUTS` — the table the CLI already holds — so that "is this a complete +rule" is answered from data rather than from per-engine prose. + +A response entry carrying the legacy single `content` object SHALL remain valid and SHALL continue +to be filed as an ast-grep rule. `files` and `content` SHALL be mutually exclusive. + +#### Scenario: A runtime rule arrives complete + +- **WHEN** a delivered rule declares engine `runtime` and carries `check.ts` and `captures/*.yml` +- **THEN** the CLI SHALL write them under `.taskless/rules/runtime//` +- **AND** the rule SHALL be discoverable and verifiable without further input + +#### Scenario: A Vale rule arrives with its config + +- **WHEN** a delivered rule declares engine `vale` and carries `.yml` and `.vale.ini` +- **THEN** the CLI SHALL write both +- **AND** the rule SHALL be scoped by its own `.vale.ini` rather than by a synthesized default + +#### Scenario: An incomplete file set is refused + +- **WHEN** a delivered file set omits a file the engine layout requires +- **THEN** the CLI SHALL refuse the rule and name what is missing +- **AND** SHALL NOT write a partial rule directory + +### Requirement: Delivered paths are refused before they are written + +The CLI SHALL reject an absolute path, any `..` segment, and any path the engine layout does not +account for, **before** creating any directory or file. Writing server-supplied paths is a +directory-traversal surface that did not exist while the response carried one structured object +whose destination the client computed itself. + +#### Scenario: A traversing path is refused + +- **WHEN** a delivered file declares a path containing `..` or an absolute path +- **THEN** the CLI SHALL refuse the entire rule +- **AND** SHALL NOT have created any file or directory for it diff --git a/openspec/specs/cli-layout-export/spec.md b/openspec/specs/cli-layout-export/spec.md new file mode 100644 index 00000000..344764eb --- /dev/null +++ b/openspec/specs/cli-layout-export/spec.md @@ -0,0 +1,35 @@ +# cli-layout-export Specification + +## Purpose + +TBD - created by archiving change generator-payload-alignment. Update Purpose after archive. + +## Requirements + +### Requirement: The rule layout is published as data + +The CLI SHALL publish its rule-layout table — the known engines, each engine's rule file, config +file and captures directory, and the rules and tests directory names — as an importable module, +so that a service building a rule payload validates against the table rather than transcribing it. + +A shape documented in prose drifts: the same layout was described in seven stale code comments and +in the runtime-execution spec, all naming a pre-migration path. A shape published as data does not. + +#### Scenario: The layout is importable + +- **WHEN** a consumer imports the published layout entry +- **THEN** it SHALL receive the engine list and each engine's layout as values +- **AND** the values SHALL be the same ones the CLI itself dispatches on + +### Requirement: The published layout carries no CLI runtime + +The published entry SHALL NOT reach the filesystem, the network, telemetry, or the command tree, +so a Worker can import it. This SHALL be enforced by the build — the published chunk graph is +checked at build time, failing the build rather than relying on review — matching how the prompts +entry is already constrained. + +#### Scenario: A runtime import fails the build + +- **WHEN** the published layout graph reaches a host capability +- **THEN** the build SHALL fail +- **AND** the artifact SHALL NOT be emitted diff --git a/openspec/specs/cli-rule-reconciliation/spec.md b/openspec/specs/cli-rule-reconciliation/spec.md index 5d4c1170..c56f389e 100644 --- a/openspec/specs/cli-rule-reconciliation/spec.md +++ b/openspec/specs/cli-rule-reconciliation/spec.md @@ -222,3 +222,43 @@ signal. The server-side record is authoritative and the server decides what runs - **WHEN** a rule file has a locally stored signature that matches its content - **THEN** the CLI SHALL NOT run the file on that basis alone - **AND** SHALL rely on the server's `run` set for authorization + +### Requirement: The reported file path is contractual + +The CLI SHALL report each runtime check to reconcile as a **repo-root-relative POSIX path**, +`.taskless/rules/runtime//check.ts`, with platform separators normalized so every host +reports the same string for the same rule. + +This is a cross-team contract, not an implementation detail. Reconcile matches `run` by content +digest and is path-independent, but the `unsafe` versus `unknown` split is a lookup on the +reported name: a spelling mismatch downgrades a tampered file from `unsafe` ("content changed in +place") to `unknown` ("never issued") — a diagnostic loss on the one path where the diagnosis +matters. + +#### Scenario: The reported path is repo-relative and POSIX + +- **WHEN** the CLI reports a runtime check to reconcile +- **THEN** the `file` SHALL be `.taskless/rules/runtime//check.ts` +- **AND** the separators SHALL be `/` regardless of host platform + +### Requirement: A withheld rule can be re-fetched + +When reconcile reports a rule as `unsafe` or `unknown`, the CLI SHALL be able to request the +blessed bytes for that rule rather than only warning. The request SHALL carry the rule id **and** +the signature the client holds, scoped like reconcile itself, so that a bare digest cannot be used +to retrieve content across organizations. + +The response SHALL be the bytes matching the held signature, never the newest generation. +Answering with newer bytes would upgrade a rule in the middle of a `check` without anyone asking; +upgrading is regeneration and SHALL remain an explicit action. + +#### Scenario: An unsafe rule is repaired + +- **WHEN** reconcile reports a rule as `unsafe` +- **THEN** the CLI SHALL be able to re-fetch the blessed bytes for that rule +- **AND** the rule SHALL reconcile as `run` after the bytes are restored + +#### Scenario: Re-fetch does not upgrade + +- **WHEN** a newer generation of the same rule exists server-side +- **THEN** re-fetch SHALL still return the bytes matching the signature the client reported diff --git a/openspec/specs/cli-rules/spec.md b/openspec/specs/cli-rules/spec.md index 0598e917..0353ff95 100644 --- a/openspec/specs/cli-rules/spec.md +++ b/openspec/specs/cli-rules/spec.md @@ -206,12 +206,26 @@ The API calls for rule generation (`POST /cli/api/request` and `GET /cli/api/req `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. +A rule id does not carry its engine, so the CLI SHALL **resolve** which engine directory holds `` rather than assuming one. A rule id is globally unique by construction, so at most one engine can hold it. When no engine holds the id, the CLI SHALL report not-found without naming an engine, because naming one would be a guess. + #### 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 +#### Scenario: Deleting a rule filed under any engine + +- **WHEN** a rule with id `` exists under `.taskless/rules/vale//` or `.taskless/rules/runtime//` +- **THEN** `taskless rule delete ` SHALL remove that directory +- **AND** SHALL NOT report not-found for a rule that is present on disk + +#### Scenario: Deleting an id no engine holds + +- **WHEN** no engine directory contains `` +- **THEN** the CLI SHALL report the rule was not found under `.taskless/rules/` +- **AND** the message SHALL NOT name a single engine's path + ### Requirement: Rules delete does not require authentication `taskless rule delete` SHALL NOT require authentication. Deleting a local file is not a service operation. (Renamed.) diff --git a/openspec/specs/cli-runtime-rule-execution/spec.md b/openspec/specs/cli-runtime-rule-execution/spec.md index b4b8d546..78e8a8a7 100644 --- a/openspec/specs/cli-runtime-rule-execution/spec.md +++ b/openspec/specs/cli-runtime-rule-execution/spec.md @@ -8,26 +8,46 @@ Defines how the CLI executes a **runtime rule** in `taskless check`: the on-disk ### Requirement: Runtime rules are directories recognized by metadata -The CLI SHALL recognize a **runtime rule** as a directory under `.taskless/runtime/rules//` -containing one or more ast-grep capture `*.yml` (one per capture rule) and a single `check.ts`, -its capture rules declaring `metadata.taskless.kind: runtime`. The rule's check file SHALL be -the `check.ts` in the rule directory. The CLI SHALL read **each capture rule's** -`metadata.taskless.match` (`anchor` or `broad`) to select that capture rule's ast-grep -invocation mode; capture rules within one runtime rule MAY mix modes (each is independent). -Rule files under `.taskless/sg/rules/` SHALL continue to be treated as static ast-grep rules, -not runtime rules. `.taskless/runtime/rule-tests//` holds `valid/` and `invalid/` -verification fixtures and SHALL NOT be executed by `check`. +The CLI SHALL recognize a **runtime rule** as a directory under `.taskless/rules/runtime//` +containing a `captures/` directory of one or more ast-grep capture `*.yml` (one per capture rule) +and a single `check.ts`, its capture rules declaring `metadata.taskless.kind: runtime`. The rule's +check file SHALL be the `check.ts` in the rule directory. The CLI SHALL read **each capture rule's** +`metadata.taskless.match` to select that capture rule's ast-grep invocation mode; capture rules +within one runtime rule MAY mix modes (each is independent). + +A `match` value the build does not implement SHALL be **refused**, never coerced to a default. The +modes scan different things — `anchor` is a syntactic narrow, `broad` a whole-language enumerator — +so substituting one for another reinterprets the capture rather than degrading it, and the shortfall +would be reported as a clean pass. The refusal SHALL be per capture file, not per rule, so one +unimplemented mode does not remove a rule's other narrows. + +Rule files under `.taskless/rules/sg/` SHALL continue to be treated as static ast-grep rules, +not runtime rules. A rule's `.tests/` directory holds verification fixtures and SHALL NOT be +executed by `check`. #### Scenario: A runtime directory entry is a runtime rule -- **WHEN** `.taskless/runtime/rules//` contains capture `*.yml` with `metadata.taskless.kind: runtime` and a `check.ts` +- **WHEN** `.taskless/rules/runtime//captures/` contains `*.yml` with `metadata.taskless.kind: runtime` and the directory has a `check.ts` - **THEN** the CLI SHALL treat it as a runtime rule with `check.ts` as its check file and route it to the runtime harness #### Scenario: Rules under sg remain static -- **WHEN** a rule file lives under `.taskless/sg/rules/` +- **WHEN** a rule file lives under `.taskless/rules/sg/` - **THEN** the CLI SHALL treat it as a static rule and SHALL NOT route it to the runtime harness +#### Scenario: An unimplemented match mode is refused + +- **WHEN** a capture rule declares a `match` value this build does not implement +- **THEN** the CLI SHALL NOT load that capture rule +- **AND** SHALL NOT treat it as `anchor` +- **AND** the rule's other capture rules SHALL still load + +#### Scenario: verify explains a refused capture + +- **WHEN** `taskless verify` inspects a rule holding a capture with an unimplemented `match` value +- **THEN** verification SHALL fail +- **AND** the error SHALL name the capture file, the offending value, and the modes this build implements + ### Requirement: The harness narrows with one ast-grep scan and gates on matches For a runtime rule the CLI SHALL assemble the rule's capture rules into an ast-grep @@ -140,3 +160,50 @@ blessed (read-hash-execute ordering). - **WHEN** a runtime rule is blessed and executed - **THEN** the CLI SHALL invoke the `check.ts` materialized under `.taskless/.run/` - **AND** SHALL NOT execute a copy modified in `.taskless/runtime-rules/` after reconciliation + +### Requirement: A runtime rule has exactly one executable file + +The CLI SHALL refuse a runtime rule whose directory contains any module file other than +`check.ts`. `check.ts` is the only executable surface of a runtime rule and the only artifact +carrying a signature, so any other module would be code reachable from a blessed entry point +without itself being blessed — one relative import away, while tampering with it leaves +`check.ts` still matching its blessed digest. The generator commits to emitting exactly one; +this requirement makes the guarantee enforced rather than trusted. + +"Module file" SHALL cover every extension a check could import, not only `.ts`: the loader +transpiles TypeScript, but a `.js` sibling resolves just as readily. + +The search SHALL be recursive. "One import away" is not "one directory away" — a single +`import "./lib/helper.ts"` reaches an arbitrarily deep relative path in one hop — so a search +bounded to the rule root would let any nested directory carry unsigned code past the check while +`check.ts` still matched its blessed signature. The search SHALL also consider only files, so a +directory whose name ends in a module extension is not reported as one. + +The rule's `.tests/` directory SHALL be excluded from this search. A runtime check reads real +files under a root, so a fixture that is itself TypeScript is the normal case rather than a +smuggled helper, and refusing it would break correct rules — a worse failure than the one being +prevented. + +#### Scenario: A helper module beside check.ts is refused + +- **WHEN** a runtime rule directory contains `check.ts` and any other module file, at any depth +- **THEN** the CLI SHALL refuse the rule and name the offending file +- **AND** SHALL NOT execute its `check.ts` + +#### Scenario: A TypeScript test fixture is not a stray module + +- **WHEN** a runtime rule carries a `.ts` file under its `.tests/` directory +- **THEN** the CLI SHALL still discover and run the rule + +### Requirement: Declared versions are read + +The CLI SHALL read `RUNTIME_CHECK_PROTOCOL_VERSION` against a delivered check's declared protocol +version, and `metadata.taskless.version` on a capture rule, refusing what it does not implement. +Both values exist today and are read by nothing, so a payload declaring a future contract is +executed against the current one. + +#### Scenario: A future protocol version is refused + +- **WHEN** a runtime rule declares a check protocol version this build does not implement +- **THEN** the CLI SHALL refuse the rule and state the version it expected +- **AND** SHALL NOT invoke the check with the current argument shape From 01333c4469fbf1b72ed3af8560fbf2904a1821a9 Mon Sep 17 00:00:00 2001 From: Jakob Heuser Date: Tue, 1 Sep 2026 21:02:55 -0700 Subject: [PATCH 2/2] fix(openspec): reconcile the standing specs on one runtime layout Archiving promoted requirements naming `.taskless/rules/runtime//` into capabilities whose untouched neighbours still named older paths, so the official spec contradicted itself about where a runtime rule's check.ts lives. Three schemes were in the standing specs at once: `.taskless/runtime-rules/` (pre-0004), `.taskless/runtime/rules/` (post-0004, pre-0005), and the current `.taskless/rules/runtime//`. The check dispatch requirement carried the 0004-era spelling for all three engines, not just runtime. Corrected the statements describing current behaviour, in cli-check, cli-rule-reconciliation and cli-runtime-rule-execution. Left cli-rule-format alone deliberately. Its references describe what migration 0004 does, and 0004 still maps `runtime-rules/` to `runtime/rules/` today (0004-rule-directories.ts:112), so they are accurate history rather than stale current-behaviour. Rewriting them would falsify the migration they document. Task 1.5 claimed this was handled and it was not: the delta corrected only the requirement it touched and left `Blessed runtime rules execute from the materialized run directory` in the same file naming the old path. Its text now says what it actually did. --- .../archive/2026-09-02-generator-payload-alignment/tasks.md | 2 +- openspec/specs/cli-check/spec.md | 6 +++--- openspec/specs/cli-rule-reconciliation/spec.md | 4 ++-- openspec/specs/cli-runtime-rule-execution/spec.md | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md b/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md index af23ea30..6ca617b8 100644 --- a/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md +++ b/openspec/changes/archive/2026-09-02-generator-payload-alignment/tasks.md @@ -9,7 +9,7 @@ green on its own; none depends on a later one to be correct. - [x] 1.2 Make the `delete` not-found message engine-agnostic - [x] 1.3 Correct the seven stale `.taskless//rules/` comments, leaving the two historical ones in the migrations - [x] 1.4 Test deleting a rule filed under each engine, and an id no engine holds -- [x] 1.5 Correct the same stale layout in `cli-runtime-rule-execution`'s spec text (delta written; lands with this change) +- [x] 1.5 Correct the same stale layout in `cli-runtime-rule-execution`'s spec text. **Partially done by the delta**: it corrected only the requirement the delta itself touched, and left the `Blessed runtime rules execute from the materialized run directory` requirement in the same file still naming `.taskless/runtime-rules/`. The remaining sites, here and in `cli-check` and `cli-rule-reconciliation`, were corrected in the archive PR that promoted this change ## 2. Publish the layout table (slice 2) — unblocks the generator diff --git a/openspec/specs/cli-check/spec.md b/openspec/specs/cli-check/spec.md index a76c0073..ae15db09 100644 --- a/openspec/specs/cli-check/spec.md +++ b/openspec/specs/cli-check/spec.md @@ -258,7 +258,7 @@ authentication. `taskless check` SHALL reconcile before running runtime rules whenever a bearer token and a `repositoryUrl` are resolvable and `--anonymous` is not set. It SHALL compute the signature -envelope for the `check.ts` of every runtime rule under `.taskless/runtime-rules/`, call +envelope for the `check.ts` of every runtime rule under `.taskless/rules/runtime/`, call `POST /cli/api/reconcile` with `{ repositoryUrl, files }`, and then execute **only** the runtime rules whose `check.ts` is returned in the `run` set, matched back to local files by signature (per the `cli-rule-reconciliation` capability). Capture `*.yml` and static rules @@ -298,11 +298,11 @@ non-zero code solely because reconciliation failed, and the warning SHALL be sup ### Requirement: Check dispatches static and runtime rules to distinct executors -`taskless check` SHALL dispatch rules to distinct executors by their engine directory: **ast-grep** rules under `.taskless/sg/` via the ast-grep scanner, **Vale** rules under `.taskless/vale/` via the Vale runner (per the `cli-vale-rule-engine` capability), and **runtime** rules under `.taskless/runtime/rules/` via the runtime harness (per the `cli-runtime-rule-execution` capability). Findings from all executors SHALL be aggregated into the same result set and SHALL count toward the exit code identically. +`taskless check` SHALL dispatch rules to distinct executors by their engine directory: **ast-grep** rules under `.taskless/rules/sg/` via the ast-grep scanner, **Vale** rules under `.taskless/rules/vale/` via the Vale runner (per the `cli-vale-rule-engine` capability), and **runtime** rules under `.taskless/rules/runtime/` via the runtime harness (per the `cli-runtime-rule-execution` capability). Findings from all executors SHALL be aggregated into the same result set and SHALL count toward the exit code identically. #### Scenario: Mixed corpus runs all executors -- **WHEN** `.taskless/sg/` contains ast-grep rules, `.taskless/vale/` contains Vale rules, and `.taskless/runtime/rules/` contains runtime rules +- **WHEN** `.taskless/rules/sg/` contains ast-grep rules, `.taskless/rules/vale/` contains Vale rules, and `.taskless/rules/runtime/` contains runtime rules - **THEN** the CLI SHALL run ast-grep rules through `sg scan`, Vale rules through the Vale runner, and runtime rules through the runtime harness - **AND** SHALL merge their findings into one result set diff --git a/openspec/specs/cli-rule-reconciliation/spec.md b/openspec/specs/cli-rule-reconciliation/spec.md index c56f389e..e6e7fb6f 100644 --- a/openspec/specs/cli-rule-reconciliation/spec.md +++ b/openspec/specs/cli-rule-reconciliation/spec.md @@ -112,13 +112,13 @@ inert data, always available, and SHALL NOT be reported to or gated by reconcili SHALL reconcile by sending `POST /cli/api/reconcile` with an `Authorization: Bearer ` header and a JSON body `{ repositoryUrl, files }`, where `repositoryUrl` is the full repository URL and `files` is an array of `{ file, signature }` covering the `check.ts` of **every** -runtime rule the CLI holds under `.taskless/runtime-rules/`. `file` SHALL be the `check.ts`'s +runtime rule the CLI holds under `.taskless/rules/runtime/`. `file` SHALL be the `check.ts`'s delivered path as it exists on disk and `signature` SHALL be the full envelope computed for its bytes. The CLI SHALL send the whole signature envelope, not a bare digest. #### Scenario: Every runtime rule's check.ts is reported -- **WHEN** the CLI reconciles with runtime rules present under `.taskless/runtime-rules/` +- **WHEN** the CLI reconciles with runtime rules present under `.taskless/rules/runtime/` - **THEN** the request body SHALL include one `{ file, signature }` entry for the `check.ts` of each runtime rule #### Scenario: Inert files are not reported diff --git a/openspec/specs/cli-runtime-rule-execution/spec.md b/openspec/specs/cli-runtime-rule-execution/spec.md index 78e8a8a7..e8b67b15 100644 --- a/openspec/specs/cli-runtime-rule-execution/spec.md +++ b/openspec/specs/cli-runtime-rule-execution/spec.md @@ -152,14 +152,14 @@ toward the exit code identically to static findings. `Finding.severity` (`error` When a runtime rule is executed on a validated path, the CLI SHALL execute it from the ephemeral, gitignored `.taskless/.run/` materialization of the blessed bytes, not from the -live `.taskless/runtime-rules/` tree, so the bytes executed are the exact bytes reconciliation +live `.taskless/rules/runtime/` tree, so the bytes executed are the exact bytes reconciliation blessed (read-hash-execute ordering). #### Scenario: Execution uses the blessed bytes - **WHEN** a runtime rule is blessed and executed - **THEN** the CLI SHALL invoke the `check.ts` materialized under `.taskless/.run/` -- **AND** SHALL NOT execute a copy modified in `.taskless/runtime-rules/` after reconciliation +- **AND** SHALL NOT execute a copy modified in `.taskless/rules/runtime/` after reconciliation ### Requirement: A runtime rule has exactly one executable file