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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 7 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ concurrency:
cancel-in-progress: ${{ github.event_name != 'push' }}

jobs:
# Prettier, eslint, and typecheck share one runner: one checkout and one
# install instead of three of each. Prettier and eslint still run un-cached
# in CI: restored result caches can mark files clean against a stale tool
# version or config, masking real failures. The --cache flags in the
# package.json lint script remain for local speed.
# oxfmt, oxlint, and typecheck share one runner: one checkout and one
# install instead of three of each. Dummy job names prettier and eslint
# stay for protect-main.
static-analysis:
runs-on: ubuntu-latest
steps:
Expand All @@ -39,11 +37,11 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

- name: Prettier
run: bunx prettier --check .
- name: oxfmt
run: bunx oxfmt --check .

- name: ESLint
run: bunx eslint .
- name: oxlint
run: bunx oxlint .

- name: Typecheck
run: bun run typecheck
Expand Down
28 changes: 28 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"trailingComma": "all",
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"printWidth": 80,
"sortPackageJson": false,
"sortImports": false,
"ignorePatterns": [
"dist/**",
"vendor/**",
".worktrees/**",
"scratch/**",
"node_modules/**",
"CHANGELOG.md",
"tmp/**",
".claude/**",
".tmp/**",
"plugins/corbits-skills/skills/opsh/SKILL.md",
"plugins/corbits-skills/skills/refactor/SKILL.md",
"plugins/corbits-skills/skills/scribe/SKILL.md",
"plugins/corbits-skills/skills/ast-grep/SKILL.md",
"plugins/corbits-skills/skills/review/SKILL.md",
"plugins/corbits-skills/skills/create-issue/SKILL.md"
]
}
106 changes: 106 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
// Type-aware rules (no-floating-promises, no-misused-promises,
// no-unsafe-type-assertion) stay on tsc until oxlint can honor them.
"plugins": ["eslint", "typescript"],
"jsPlugins": ["./scripts/oxlint-plugin-corbits.js"],
"categories": {
"correctness": "error"
},
"ignorePatterns": [
"dist/**",
"vendor/**",
".worktrees/**",
"**/.worktrees/**",
".scratch/**",
"**/.scratch/**",
"scratch/**",
"**/scratch/**",
"tmp/**",
"**/tmp/**",
".claude/**",
"**/.claude/**",
".tmp/**",
"**/.tmp/**",
"node_modules/**",
"**/node_modules/**"
],
"options": {
"reportUnusedDisableDirectives": "error"
},
"rules": {
"no-console": "error",
"no-unused-vars": [
"error",
{
"args": "all",
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
],
"no-unused-expressions": ["error", { "allowTaggedTemplates": true }],
"typescript/adjacent-overload-signatures": "error",
"typescript/array-type": "error",
"typescript/ban-ts-comment": "error",
"typescript/ban-tslint-comment": "error",
"typescript/class-literal-property-style": "error",
"typescript/consistent-generic-constructors": "error",
"typescript/consistent-indexed-object-style": "error",
"typescript/consistent-type-assertions": "error",
"typescript/consistent-type-definitions": "off",
"typescript/no-confusing-non-null-assertion": "error",
"typescript/no-duplicate-enum-values": "error",
"typescript/no-dynamic-delete": "error",
"typescript/no-empty-interface": "error",
"typescript/no-empty-object-type": "error",
"typescript/no-explicit-any": "error",
"typescript/no-extra-non-null-assertion": "error",
"typescript/no-extraneous-class": "error",
"typescript/no-inferrable-types": "error",
"typescript/no-invalid-void-type": "error",
"typescript/no-misused-new": "error",
"typescript/no-namespace": "error",
"typescript/no-non-null-asserted-nullish-coalescing": "error",
"typescript/no-non-null-asserted-optional-chain": "error",
"typescript/no-non-null-assertion": "error",
"typescript/no-this-alias": "error",
"typescript/no-unnecessary-type-constraint": "error",
"typescript/no-unsafe-declaration-merging": "error",
"typescript/no-unsafe-function-type": "error",
"typescript/no-wrapper-object-types": "error",
"typescript/prefer-as-const": "error",
"typescript/prefer-for-of": "error",
"typescript/prefer-function-type": "error",
"typescript/prefer-literal-enum-member": "error",
"typescript/prefer-namespace-keyword": "error",
"typescript/unified-signatures": "error",
"no-empty-function": "error"
},
"overrides": [
{
"files": ["src/util/control-char-strip.ts"],
"rules": {
"no-control-regex": "off"
}
},
{
"files": ["scripts/**"],
"rules": {
"no-console": "off"
}
},
{
"files": ["src/tui/smoke.ts", "src/tui/demo.ts"],
"rules": {
"no-console": "off"
}
},
{
"files": ["**/*.test.ts"],
"rules": {
"corbits/no-bare-mock-module": "error"
}
}
]
}
16 changes: 0 additions & 16 deletions .prettierignore

This file was deleted.

5 changes: 0 additions & 5 deletions .prettierrc.json

This file was deleted.

4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ When refactoring replaces an old path, delete the old one. No back-compat shims,
- Bug fixes start with a failing test that reproduces the bug. Do not start by patching.
- `tests/unit/` shared unit tests and helpers · co-located `src/**/*.test.ts` for module logic · `tests/fixtures/` fixture repos · `tests/integration/` reactor/permission harness. Planned: `tests/e2e/` (fixture-repo runs).
- A test must not depend on another file having run, or on the default file order. It must pass under `bun test ./src ./tests ./evals ./scripts --randomize`. If a test mutates module-level state or calls `mock.module`, it must restore that state itself (`afterEach`/`afterAll`), not rely on the process happening to reset it. When capturing a module's real exports to restore later, shallow-copy them (`{ ...moduleNamespace }`) at capture time, whether the namespace came from `await import(path)` or a static `import * as ns from "path"` — Bun mutates the live namespace object in place when the module is mocked, so holding a bare reference to it (either form) silently turns into the mocked exports.
- Never call `mock.module` directly. Bun runs every test file in one process, so a `mock.module` call without its own teardown stays installed for the rest of the run and silently replaces the real module for other files — producing failures in files the change never touched, with no obvious link to the cause and no signal from `tsc` or a per-file run (CL-6967). Use `withMockedModule`/`withMockedModuleDuring` from `tests/helpers/mock-module.ts`, which capture the real module and register their own restore. An eslint rule (`no-restricted-syntax` in `eslint.config.js`) rejects bare `mock.module` calls in `*.test.ts` files.
- A test earns its place only if a real behavior change can fail it. Document copy, brand colors, marketing assets, and splash text are not behavior: assertions that pin an asset's literal wording, an exact palette hex/ANSI value, or rendered copy fail on copy/design edits and catch no regressions — assert the contract instead (parsing, formatting, ranges, aliases, invariants). Tests are code too: pinning a source file's own text is the same trap. This bar is a review and authorship rule, not an eslint shape match.
- Never call `mock.module` directly. Bun runs every test file in one process, so a `mock.module` call without its own teardown stays installed for the rest of the run and silently replaces the real module for other files — producing failures in files the change never touched, with no obvious link to the cause and no signal from `tsc` or a per-file run (CL-6967). Use `withMockedModule`/`withMockedModuleDuring` from `tests/helpers/mock-module.ts`, which capture the real module and register their own restore. The oxlint plugin (`corbits/no-bare-mock-module` in `.oxlintrc.json` / `scripts/oxlint-plugin-corbits.js`) rejects bare `mock.module` calls in `*.test.ts` files.
- A test earns its place only if a real behavior change can fail it. Document copy, brand colors, marketing assets, and splash text are not behavior: assertions that pin an asset's literal wording, an exact palette hex/ANSI value, or rendered copy fail on copy/design edits and catch no regressions — assert the contract instead (parsing, formatting, ranges, aliases, invariants). Tests are code too: pinning a source file's own text is the same trap. This bar is a review and authorship rule, not a linter shape match.

## Build & Validation

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ parallel copies under `docs/` or `scripts/notes/`. At cut time: rename

### Changed

- oxfmt formats the tree and oxlint is the lint gate. Prettier and ESLint are
gone. Dummy CI job names `prettier` and `eslint` stay for protect-main.
Empty functions and non-null assertions are errors.

- Skywalker may spawn one successor with a changed brief after a failed or
incomplete-report fleet worker. A parent-initiated interrupt
(`stop_reason: interrupted`) is a resumable pause — `resume_agent` or
Expand Down
Loading
Loading