Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Runs every shipped demonstration rule's own fixtures.
# Runs every conformance corpus rule's own fixtures, as a smoke test.
#
# ## Why this is its own workflow
#
Expand All @@ -18,10 +18,10 @@
# conformance corpus published as `@taskless/cli/reference.json`, and
# `test/reference.test.ts` only proves that payload matches the asset trees.
# This job proves the trees themselves hold: it copies the manifest's own
# written-path list, read straight off `assets/demo-*/`, into one bare project
# written-path list, read straight off `assets/reference-*/`, into one bare project
# and runs the engines over it. A corpus rule that fails its own cases would
# hand another team a broken oracle.
name: Demo rules
name: Reference corpus

on:
push:
Expand All @@ -43,8 +43,8 @@ env:
TASKLESS_TELEMETRY_DISABLED: "1"

jobs:
demo-rules:
name: Demo rules
reference-corpus:
name: Reference corpus
runs-on: ubuntu-latest

steps:
Expand Down Expand Up @@ -81,7 +81,7 @@ jobs:
# The script writes the file itself. Redirecting its stdout here
# captured pnpm's platform warnings alongside the manifest, and the
# loop below then tried to stage a rule named "WARN".
pnpm --filter @taskless/cli demo:manifests /tmp/manifests.txt
pnpm --filter @taskless/cli corpus:manifests /tmp/manifests.txt
cat /tmp/manifests.txt
# The trailing field is the manifest's own written-path list, so this
# stages exactly the rule and its cases. A `cp -R` of the asset
Expand Down Expand Up @@ -119,5 +119,5 @@ jobs:
if (rule.ran !== true) throw new Error(id + " fixtures did not run: " + JSON.stringify(rule));
if (rule.ok !== true) throw new Error(id + " fixtures failed: " + JSON.stringify(rule.errors));
}
console.log("all " + expected.length + " demo rules ran and passed");
console.log("all " + expected.length + " reference rules ran and passed");
'
4 changes: 2 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ export default tseslint.config(
"plugins/",
"openspec/",
"**/test/fixtures/",
// The demonstration rule ships as DATA, not as source. Its `check.ts`
// The runtime reference rule ships as DATA, not as source. Its `check.ts`
// is a rule in the published conformance corpus, run under `tsx`
// against a structural contract and importing nothing from this
// repository — so it is deliberately outside every tsconfig, and
// type-aware linting has no project to resolve it against.
"packages/cli/assets/demo-runtime/",
"packages/cli/assets/reference-runtime/",
"tmp/",
// Worktrees are full checkouts nested inside the repo. Without this, a
// root `eslint .` lints every worktree's copy of the tree — slow, and it
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"build": "vite build && tsc -p tsconfig.public.json",
"build:nightly": "TASKLESS_BUILD_TARGET=nightly vite build && tsc -p tsconfig.public.json",
"build:self": "TASKLESS_BUILD_TARGET=self vite build",
"demo:manifests": "tsx scripts/print-demo-manifests.ts",
"corpus:manifests": "tsx scripts/print-corpus-manifests.ts",
"generate:api": "pnpm generate:api:schema && pnpm generate:api:types",
"generate:api:schema": "tsx scripts/fetch-api-schema.ts",
"generate:api:types": "openapi-typescript src/generated/api-v2.schema.json -o src/generated/api-v2.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Write each shipped demo rule as
* Write each conformance corpus rule as
* `<engine> <ruleId> <assetDirectory> <path>...`.
*
* Exists so CI can stage every sample without restating the list in YAML,
Expand All @@ -17,19 +17,19 @@
* reading it then tried to stage a rule called `WARN`. A file the script writes
* itself cannot pick up a caller's noise.
*
* Run: pnpm --filter @taskless/cli demo:manifests <path>
* Run: pnpm --filter @taskless/cli corpus:manifests <path>
*/

import { writeFile } from "node:fs/promises";

import { DEMO_MANIFESTS, writtenPaths } from "../src/rules/demo/manifest";
import { CORPUS_MANIFESTS, writtenPaths } from "../src/rules/corpus/manifest";

const target = process.argv[2];
if (target === undefined) {
throw new Error("usage: print-demo-manifests <output-path>");
throw new Error("usage: print-corpus-manifests <output-path>");
}

const lines = DEMO_MANIFESTS.map(
const lines = CORPUS_MANIFESTS.map(
(manifest) =>
`${manifest.engine} ${manifest.ruleId} ${manifest.assetDirectory} ${writtenPaths(manifest).join(" ")}`
);
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/scripts/write-reference.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Regenerate `assets/reference.json` from the shipped rules.
*
* The corpus is every demonstration rule with the prompt it answers, the rule
* The corpus is every reference rule with the prompt it answers, the rule
* itself, and the held-out cases — separately, so another team can run their
* rule against our cases and ours against theirs.
*
Expand All @@ -15,7 +15,7 @@ import { writeFile } from "node:fs/promises";
import { join } from "node:path";

import { buildReference } from "../src/rules/reference";
import { readReferenceInputs } from "../src/rules/demo/manifest";
import { readReferenceInputs } from "../src/rules/corpus/manifest";

const assetsRoot = join(import.meta.dirname, "..", "assets");

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* The demonstration rules behind the conformance corpus: their identities,
* The rules behind the conformance corpus: their identities,
* their file lists, and the one reader that loads them off disk.
*
* Nothing in the bundle imports this. The rules used to be written into a
Expand All @@ -25,8 +25,8 @@ import { join } from "node:path";
import type { EngineName } from "../layout";
import type { ReferenceInput } from "../reference";

/** One shipped demonstration rule. */
export interface DemoManifest {
/** One rule in the conformance corpus. */
export interface CorpusManifest {
engine: EngineName;
/**
* The rule's directory name, and the id `check` and `verify` address it by.
Expand Down Expand Up @@ -72,19 +72,19 @@ export interface DemoManifest {
assetDirectory: string;
}

export const DEMO_MANIFESTS: readonly DemoManifest[] = [
export const CORPUS_MANIFESTS: readonly CorpusManifest[] = [
{
engine: "sg",
ruleId: "no-eval-call",
assetDirectory: "demo-sg",
assetDirectory: "reference-sg",
promptPath: "prompt.md",
rulePaths: ["no-eval-call.yml"],
testPaths: [".tests/no-eval-call-test.yml"],
},
{
engine: "vale",
ruleId: "prefer-use-over-utilize",
assetDirectory: "demo-vale",
assetDirectory: "reference-vale",
promptPath: "prompt.md",
// `.vale.ini` is the rule's own engine config, so it is part of what a
// generator must produce rather than part of the oracle.
Expand All @@ -94,7 +94,7 @@ export const DEMO_MANIFESTS: readonly DemoManifest[] = [
{
engine: "runtime",
ruleId: "env-keys-declared",
assetDirectory: "demo-runtime",
assetDirectory: "reference-runtime",
promptPath: "prompt.md",
rulePaths: ["check.ts", "captures/env-read.yml"],
testPaths: [
Expand All @@ -107,7 +107,7 @@ export const DEMO_MANIFESTS: readonly DemoManifest[] = [
];

/** Every file of the staged rule tree: the rule and its cases, never the prompt. */
export function writtenPaths(manifest: DemoManifest): readonly string[] {
export function writtenPaths(manifest: CorpusManifest): readonly string[] {
return [...manifest.rulePaths, ...manifest.testPaths];
}

Expand All @@ -126,7 +126,7 @@ export async function readReferenceInputs(
);

return Promise.all(
DEMO_MANIFESTS.map(async (manifest) => ({
CORPUS_MANIFESTS.map(async (manifest) => ({
engine: manifest.engine,
ruleId: manifest.ruleId,
prompt: await read(manifest.assetDirectory, manifest.promptPath),
Expand Down
12 changes: 6 additions & 6 deletions packages/cli/test/reference.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import {
} from "../src/rules/reference";
import { RULE_CONSTRAINTS } from "../src/rules/constraints";
import {
DEMO_MANIFESTS,
CORPUS_MANIFESTS,
readReferenceInputs,
writtenPaths,
} from "../src/rules/demo/manifest";
} from "../src/rules/corpus/manifest";
import { ENGINE_LAYOUTS, ENGINES, type EngineName } from "../src/rules/layout";
import { ruleDirectory } from "../src/rules/engines";

Expand Down Expand Up @@ -93,7 +93,7 @@ function shapePaths(value: unknown): string[] {
return [...paths].toSorted();
}

describe("the demo reference payload", () => {
describe("the reference corpus payload", () => {
it("is current — regenerate with `pnpm --filter @taskless/cli reference`", async () => {
expect(await readReference()).toEqual(
buildReference(await readReferenceInputs(assetsRoot))
Expand All @@ -117,10 +117,10 @@ describe("the demo reference payload", () => {
// the point: whoever adds it states which of its files are dot-paths,
// rather than inheriting a guard that silently covers nothing.
expect(
DEMO_MANIFESTS.map((manifest) => manifest.engine).toSorted()
CORPUS_MANIFESTS.map((manifest) => manifest.engine).toSorted()
).toEqual(Object.keys(required).toSorted());

for (const manifest of DEMO_MANIFESTS) {
for (const manifest of CORPUS_MANIFESTS) {
const written = writtenPaths(manifest);
for (const path of required[manifest.engine] ?? []) {
expect(written, `${manifest.engine} no longer lists ${path}`).toContain(
Expand Down Expand Up @@ -414,7 +414,7 @@ describe("the reference payload is reachable by another team", () => {
/**
* One rule's worth of input, with only the fixture paths varying.
*
* Hand-built rather than mutated out of `DEMO_MANIFESTS`, so a test that
* Hand-built rather than mutated out of `CORPUS_MANIFESTS`, so a test that
* asserts a refusal cannot also alter what the real corpus publishes.
*/
function refusalInput(engine: EngineName, testPaths: string[]): ReferenceInput {
Expand Down
Loading