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
8 changes: 7 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ jobs:

- run: pnpm install

- name: Lint + Format
# `pnpm lint` is oxlint, oxfmt and the repository-wide dead-code check,
# chained with `&&`, so a finding from any of the three reddens this
# required job. The dead-code check is a step of this job rather than a
# job of its own because it needs exactly what lint already prepared, and
# a second job would pay for another checkout and install to learn the
# same thing.
- name: Lint + Format + dead code
run: pnpm lint

# The corpus is partitioned by measured per-file weight, so the shards finish
Expand Down
20 changes: 17 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ keep their own task names:
```bash
deno task verify # the shared-state interference proof
deno task verify:clean # the same proof, from a clean checkout, after the offline builds
deno task lint # oxlint + oxfmt --check
deno task lint # oxlint + oxfmt --check + the dead-code check
deno task check # typecheck
deno task test # the complete Deno suite
pnpm test:node # the complete suite under Node
Expand Down Expand Up @@ -291,8 +291,10 @@ composability runs them.

The complete battery consists of:

1. **Lint + Format**: `deno task lint` (runs `oxlint` + `oxfmt --check`) β€” must
produce 0 errors. Run `deno task fmt` to auto-fix formatting.
1. **Lint + Format + dead code**: `deno task lint` (runs `oxlint`,
`oxfmt --check` and `knip`) β€” must produce 0 errors. Run `deno task fmt` to
auto-fix formatting, and `deno task lint:dead-code` for the dead-code check
alone.
2. **Typecheck**: `deno task check` β€” must produce no errors
3. **Tests**: `deno task test` β€” the full Deno suite; CI also runs the full
corpus under Node and Bun
Expand Down Expand Up @@ -347,6 +349,18 @@ here:
lint task's command line instead β€” the rule tests in `scripts/tests/` lint
through the repository config, and an `ignorePatterns` entry there stops them
seeing their own fixtures.
- The **dead-code** half of `lint` is Knip, pinned in `package.json` and
configured in `knip.json`, exposed on its own as `deno task lint:dead-code`.
It reads the repository's import graph and fails on a source file nothing
imports, an exported value nothing uses, and an exported type nothing uses,
naming the path and the symbol. What keeps it honest is the entry-point list:
the published package exports, the runtime CLI entrypoints, every test, the
standalone scripts, the browser entry, the site's routes and islands, the
runnable examples, and the fixtures and components a document names or a test
spawns are entry points β€” nothing in the repository imports them and none of
them is dead. Anything else is implementation, and an implementation module
never becomes an entry point to silence a finding. Duplicate exports are out
of scope: `gitPlugin`/`default` is one value declared two ways, not dead code.

### Measuring the corpus

Expand Down
444 changes: 394 additions & 50 deletions deno.lock

Large diffs are not rendered by default.

105 changes: 105 additions & 0 deletions knip.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"$schema": "https://unpkg.com/knip@6/schema.json",
"ignore": [
"**/vendor/**",
"**/npm/**",
"scripts/tests/fixtures/**",
"**/_fresh/**",
"**/*.d.ts"
],
"workspaces": {
".": {
"entry": [
"scripts/*.ts",
"scripts/tests/**/*.test.ts",
"components/**/*.ts",
"smoke-test/**/*.ts",
"site/main.ts",
"site/client.ts",
"site/vite.config.ts",
"site/routes/**/*.{ts,tsx}",
"site/islands/**/*.{ts,tsx}"
],
"project": [
"scripts/**/*.ts",
"components/**/*.ts",
"smoke-test/**/*.ts",
"site/**/*.{ts,tsx}"
],
"paths": {
"@executablemd/acp": ["packages/acp/mod.ts"],
"@executablemd/acp/embedded-adapters": ["packages/acp/embedded-adapters.ts"],
"@executablemd/cli": ["packages/cli/src/deno.ts"],
"@executablemd/code-review-agent": ["packages/code-review-agent/mod.ts"],
"@executablemd/code-review-agent/review-components": [
"packages/code-review-agent/src/review-components.ts"
],
"@executablemd/core": ["packages/core/mod.ts"],
"@executablemd/core/api": ["packages/core/api.ts"],
"@executablemd/core/host": ["packages/core/host.ts"],
"@executablemd/durable-streams": ["packages/durable-streams/mod.ts"],
"@executablemd/git": ["packages/git/mod.ts"],
"@executablemd/git/api": ["packages/git/api.ts"],
"@executablemd/git/credential-helper": ["packages/git/credential-helper.ts"],
"@executablemd/git/deno": ["packages/git/deno.ts"],
"@executablemd/runtime": ["packages/runtime/mod.ts"],
"@executablemd/runtime/api": ["packages/runtime/api.ts"],
"@executablemd/runtime/files": ["packages/runtime/files.ts"],
"@executablemd/runtime/test": ["packages/runtime/test/mod.ts"],
"@executablemd/test-agent": ["packages/test-agent/mod.ts"],
"@executablemd/testing": ["packages/testing/mod.ts"],
"@executablemd/web": ["packages/web/mod.ts"],
"@executablemd/workflow": ["packages/workflow/mod.ts"],
"@executablemd/workflow/deno": ["packages/workflow/deno.ts"],
"@/*": ["site/*"]
}
},
"packages/*": {
"entry": [
"mod.ts",
"src/{deno,node,bun,compiled}.ts",
"client/main.tsx",
"tests/**/*.test.ts",
"tests/**/fixtures/**/*.{ts,tsx,js}",
"tests/**/components/**/*.ts",
"components/**/*.ts",
"demo/**/*.{ts,tsx}",
"examples/**/*.{ts,tsx}"
],
"project": [
"**/*.{ts,tsx,js,jsx}",
"!vendor/**",
"!src/repl/vendor/**",
"!npm/**"
],
"paths": {
"@executablemd/acp": ["../../packages/acp/mod.ts"],
"@executablemd/acp/embedded-adapters": ["../../packages/acp/embedded-adapters.ts"],
"@executablemd/cli": ["../../packages/cli/src/deno.ts"],
"@executablemd/code-review-agent": ["../../packages/code-review-agent/mod.ts"],
"@executablemd/code-review-agent/review-components": [
"../../packages/code-review-agent/src/review-components.ts"
],
"@executablemd/core": ["../../packages/core/mod.ts"],
"@executablemd/core/api": ["../../packages/core/api.ts"],
"@executablemd/core/host": ["../../packages/core/host.ts"],
"@executablemd/durable-streams": ["../../packages/durable-streams/mod.ts"],
"@executablemd/git": ["../../packages/git/mod.ts"],
"@executablemd/git/api": ["../../packages/git/api.ts"],
"@executablemd/git/credential-helper": ["../../packages/git/credential-helper.ts"],
"@executablemd/git/deno": ["../../packages/git/deno.ts"],
"@executablemd/runtime": ["../../packages/runtime/mod.ts"],
"@executablemd/runtime/api": ["../../packages/runtime/api.ts"],
"@executablemd/runtime/files": ["../../packages/runtime/files.ts"],
"@executablemd/runtime/test": ["../../packages/runtime/test/mod.ts"],
"@executablemd/test-agent": ["../../packages/test-agent/mod.ts"],
"@executablemd/testing": ["../../packages/testing/mod.ts"],
"@executablemd/web": ["../../packages/web/mod.ts"],
"@executablemd/workflow": ["../../packages/workflow/mod.ts"],
"@executablemd/workflow/deno": ["../../packages/workflow/deno.ts"],
"@/*": ["../../site/*"]
}
}
},
"ignoreExportsUsedInFile": true
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"@types/node": "^22.0.0",
"@types/semver": "^7.7.0",
"expect": "^30.0.0",
"knip": "6.41.0",
"oxfmt": "^0.41.0",
"oxlint": "1.74.0",
"tsx": "^4.19.0",
Expand All @@ -74,7 +75,8 @@
"test:node": "tsx scripts/runtime-tests.ts node",
"test:bun": "bun scripts/runtime-tests.ts bun",
"test:deno": "deno task test",
"lint": "oxlint -c .oxlintrc.json --ignore-pattern 'scripts/tests/fixtures/**' --ignore-pattern '**/npm/**' --ignore-pattern 'packages/workflow/vendor/cloudflare-computer-dofs/**' --ignore-pattern 'packages/acp/vendor/acpx/**' --ignore-pattern 'packages/cli/src/repl/vendor/freedom/**' packages scripts && oxfmt --check packages scripts",
"lint": "oxlint -c .oxlintrc.json --ignore-pattern 'scripts/tests/fixtures/**' --ignore-pattern '**/npm/**' --ignore-pattern 'packages/workflow/vendor/cloudflare-computer-dofs/**' --ignore-pattern 'packages/acp/vendor/acpx/**' --ignore-pattern 'packages/cli/src/repl/vendor/freedom/**' packages scripts && oxfmt --check packages scripts && pnpm run lint:dead-code",
"lint:dead-code": "knip --config knip.json --no-progress --no-config-hints --include files,exports,types",
"fmt": "oxfmt --write packages scripts"
},
"workspaces": [
Expand Down
9 changes: 0 additions & 9 deletions packages/cli/src/plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@ import type { MachineSessionAssembly } from "./session-coordinator.ts";
import { describeError } from "./props.ts";
import { renderSyntaxMarkdown } from "./syntax.ts";

/**
* The identity approved text runs under.
*
* Owned by the Component module, because the structural admission inside `<Plan>`
* reads the same bytes under the same identity: a position reading
* `(<plan>:5:1)` means the same thing whichever gate produced it.
*/
export { PLAN_IDENTITY } from "./plan-component.ts";

/** What one `xmd plan` invocation was asked to do. */
export interface PlanCommand {
/** The request, byte for byte, as fixed grammar preserved it. */
Expand Down
5 changes: 0 additions & 5 deletions packages/cli/src/release-targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,3 @@ export function releaseTargetFor(
(row) => row.platform === platform && row.architecture === architecture,
);
}

/** The row for one target triple, or `undefined` when it names none. */
export function releaseTargetNamed(target: string): ReleaseTarget | undefined {
return RELEASE_TARGETS.find((row) => row.target === target);
}
13 changes: 3 additions & 10 deletions packages/cli/src/repl/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import type { ReplPresentationRole, ReplRowStyle } from "./presentation-style.ts
import { jsonRuns, sourceRuns } from "./presentation-text.ts";
import { NO_NAVIGATION, numbered, pointLabel } from "./navigation.ts";
import type { ReplHistoryNavigation } from "./navigation.ts";
import { blankBand, EMPTY_BAND, HISTORY_ROWS } from "./history-rail.ts";
import { blankBand, EMPTY_BAND } from "./history-rail.ts";
import type { ReplPreparedRail } from "./history-rail.ts";
import { entryReading, NO_READING } from "./source-reading.ts";
import type { ReplReading } from "./source-reading.ts";
Expand All @@ -96,13 +96,7 @@ import {
resolveLocation,
surfaceForDrawer,
} from "./route.ts";
import type {
ReplDrawerRef,
ReplLiveAvailability,
ReplRoute,
ReplSelection,
ReplSurface,
} from "./route.ts";
import type { ReplDrawerRef, ReplLiveAvailability, ReplRoute, ReplSelection } from "./route.ts";
import type {
ReplAgentPermission,
ReplAgentTurn,
Expand Down Expand Up @@ -5017,5 +5011,4 @@ function totalOf(
return drawerContent(view, widths?.drawer ?? 0, preview)?.content.length ?? 0;
}

export { HISTORY_ROWS };
export type { ReplDrawerRef, ReplSurface };
export type { ReplDrawerRef };
5 changes: 0 additions & 5 deletions packages/cli/src/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1478,8 +1478,3 @@ function* suspensionEvent(
}
return undefined;
}

/** The exit code a status reports, for a caller composing its own outcome. */
export function workflowExitCode(status: WorkflowRunStatus): number {
return EXIT_BY_STATUS[status];
}
38 changes: 0 additions & 38 deletions packages/cli/tests/support/plan-harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,44 +253,6 @@ export function* useWorkingDirectory<T>(
});
}

/**
* Environment values this scope answers with, delegating every other name.
*
* Delegation matters: the agent stack reads `DEFAULT_AGENT_NAME` through the
* same Api, and answering `undefined` for everything would make a case about
* `XMD_PROPS` quietly also be a case about the default agent.
*/
export function* useEnvironment(values: Record<string, string>): Operation<void> {
yield* useRecordedEnvironment([], values);
}

/**
* The same environment, with every name it was asked recorded in order.
*
* What a resolution costs is visible only as the reads it performs: a value
* settled once and handed to both consumers reads its name once, while two
* consumers each settling their own read it twice and agree only by accident.
*/
export function* useRecordedEnvironment(
reads: string[],
values: Record<string, string>,
): Operation<void> {
yield* API.Env.around({
*env([name], next) {
reads.push(name);
if (Object.hasOwn(values, name)) {
return values[name];
}
return yield* next(name);
},
});
}

/** How many times one name was read. */
export function timesRead(reads: readonly string[], name: string): number {
return reads.filter((read) => read === name).length;
}

/**
* A temporary tree this scope creates, owns and removes whole.
*
Expand Down
10 changes: 1 addition & 9 deletions packages/core/src/agent/launch-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* value it has already moved past.
*/

import type { Agent, LaunchOptions, PermissionMode, Session } from "./agent-api.ts";
import type { Agent, PermissionMode, Session } from "./agent-api.ts";

/**
* What public launch middleware is handed.
Expand Down Expand Up @@ -241,11 +241,3 @@ export function issueLaunch(facts: LaunchFacts, generation: object): IssuedLaunc
},
};
}

/** The facts a routed request carries, for the provider that accepted it. */
export function launchOptionsOf(request: AgentLaunchRequest): LaunchOptions {
return {
agent: request.agent,
...(request.session === undefined ? {} : { session: request.session }),
};
}
23 changes: 0 additions & 23 deletions packages/core/src/body-structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,6 @@ export function bodyStructureFacts(
};
}

/** Whether these facts describe a body whose structure is refused. */
export function hasBodyStructureViolation(facts: BodyStructureFacts): boolean {
return (
facts.misplacedOutputs.length > 0 ||
facts.undeclaredReturns.length > 0 ||
facts.exclusiveOutputs.length > 0 ||
facts.missingReturn ||
facts.returnViolations.length > 0
);
}

function structureError(source: string, headline: string, violations: string[]): ErrorSegment {
const list = violations.map((entry) => ` - ${entry}`).join("\n");
return { type: "error", message: `${headline}\n${list}`, source };
Expand Down Expand Up @@ -268,18 +257,6 @@ export function renderBodyStructure(facts: BodyStructureFacts): ErrorSegment | u
return outputError ?? returnError;
}

/**
* Structural preflight (spec Β§6.9). Validates `<Output>` placement against the
* body's own source AST. Only a direct top-level `<Output>` is a valid
* declaration; any `<Output>` at depth > 0 β€” including inside unreachable or
* discarded children β€” is a placement violation. All violations are combined
* into a single aggregate ErrorSegment. Returns undefined when placement is
* valid.
*/
export function validateOutputPlacement(bodySegments: Segment[]): ErrorSegment | undefined {
return misplacedOutputAggregate(collectOutputs(bodySegments, 1));
}

/**
* Structural preflight for a body's output and return contract (spec Β§6.9,
* Β§6.10). Runs against the body's own source AST, before `<Content />`
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/component-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,6 @@ export function expandThroughTerminal(
return owned.operations.expand(request);
}

export const expand: Operations<ComponentApi>["expand"] = Component.operations.expand;
export const importComponent: Operations<ComponentApi>["importComponent"] =
Component.operations.importComponent;
export const applyModifiers: Operations<ComponentApi>["applyModifiers"] =
Expand All @@ -507,7 +506,6 @@ export const content: Operations<ComponentApi>["content"] = Component.operations
export const hasContent: Operations<ComponentApi>["hasContent"] = Component.operations.hasContent;
export const hasBinding: Operations<ComponentApi>["hasBinding"] = Component.operations.hasBinding;
export const retain: Operations<ComponentApi>["retain"] = Component.operations.retain;
export const registry: Operations<ComponentApi>["registry"] = Component.operations.registry;
export const tryContent: Operations<ComponentApi>["tryContent"] = Component.operations.tryContent;
export const hasCapture: Operations<ComponentApi>["hasCapture"] = Component.operations.hasCapture;
export const capture: Operations<ComponentApi>["capture"] = Component.operations.capture;
Expand Down
Loading
Loading