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
2 changes: 2 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ backward-compatibility impact. Write `None` only after checking each category.

- [ ] The change is focused and follows existing module boundaries.
- [ ] Tests cover the changed behavior or the missing coverage is explained.
- [ ] New or migrated tests follow the [testing policy](../CONTRIBUTING.md#testing-policy): TypeScript tests are executed and type-checked, or a concrete JavaScript exception is explained.
- [ ] Test consolidation or removal has supporting coverage and validation evidence; independent safety and architecture contracts remain protected.
- [ ] Chinese and English product copy remain aligned when applicable.
- [ ] Documentation and implementation status are updated when applicable.
- [ ] No credentials, tokens, local databases, logs, artifacts, or private paths are committed.
Expand Down
23 changes: 23 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Ling Stack Agent Instructions

## Testing

Follow the [Testing Policy](CONTRIBUTING.md#testing-policy) for all new or changed tests.

- Prefer TypeScript for new JavaScript/TypeScript-module tests and test helpers.
Ensure the owning runner discovers them and the package's validation commands and
relevant CI gates type-check them. File renames or runtime transpilation alone are
insufficient. Document concrete runtime or compatibility exceptions for JavaScript.
- Migrate existing `.mjs` tests incrementally when maintaining their owning module;
preserve coverage and test discovery, and avoid unrelated bulk conversions.
- Prefer behavior and boundary assertions over implementation-text matching. Preserve
independently valuable security, architecture, and other declared contract checks.
- Require evidence before consolidating or removing tests. Never delete tests to
reduce the JavaScript share or rewrite production code in Rust for that purpose.

## Validation

Run the narrowest relevant tests and the owning package's typecheck for test changes.
For documentation-only changes, use the checks in
[Contributing: Validation](CONTRIBUTING.md#validation). Report checks actually run and
any platform or runtime acceptance that remains unverified.
41 changes: 41 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,47 @@ only when its description is complete and its claimed validation has actually ru
states and theme switching. Include current screenshots; distinguish token
integration, behavior verification and visual approval.

## Testing Policy

### TypeScript By Default

- For JavaScript/TypeScript modules, write new tests and test helpers in TypeScript by
default, using `.test.ts` or the owning suite's `.spec.ts` convention. Use `.tsx`
when a test needs JSX. Native-language tests should use their own ecosystem's tools.
- New or migrated TypeScript tests must be discovered by the owning test runner and
included in its type-checking configuration. Wire any separate test typecheck into
the package's validation commands and relevant CI gates. Renaming a file, transpiling
it, or stripping types at runtime does not establish that it was type-checked.
- When a package currently runs only `.mjs` tests, add the minimal package-local
TypeScript execution and typecheck support with its first TypeScript test, preserving
existing coverage and test entrypoints. Keep the existing runner where practical;
a repository-wide framework replacement is not required.
- JavaScript exceptions need a concrete runtime, host, or compatibility constraint,
explained in the pull request or a short adjacent comment. An existing `.mjs`-only
test glob alone is not a reason to keep adding JavaScript tests.

### Incremental Migration

- Existing `.mjs` tests remain valid. Prefer migrating touched tests as their owning
module is maintained, with execution and type checking verified together.
- Keep migrations scoped to the module and behavior being changed. Do not perform
unrelated mass renames, lose test discovery, or reduce coverage during migration.

### Behavior And Independent Contracts

- Prefer tests that exercise observable behavior, invariants, and public boundaries.
A source keyword or call-name match alone does not prove runtime behavior or call
order. Prioritize improving these assertions when maintaining the owning module.
- Keep source-inspection tests when they independently guard a declared security,
architecture, protocol, package, release, or generated-output contract. Make the
protected contract clear; static evidence does not replace required runtime or
platform acceptance.
- Consolidate or remove tests only with evidence that they are redundant, obsolete,
or ineffective. Explain what each removed test protected, why its removal is safe,
and which retained coverage and focused validation support that conclusion.
- Do not optimize test counts or language percentages by deleting tests. A preference
for TypeScript tests is not a reason to rewrite production modules in Rust.

## Validation

For documentation-only changes:
Expand Down
3 changes: 2 additions & 1 deletion packages/daemon/test/protected-host-staging.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,8 @@ test("unknown actual bootstrap bytes close registration/current Office only, wit
});

test("more than 256 real ordinary commands and more than 64 closed stage lifetimes do not exhaust history budgets", native, async t => {
const f = createProtectedHostStageFixture(t), command = commandFor("process.exit(0)");
// Exercise real native launches without rehashing and starting Node 258 times.
const f = createProtectedHostStageFixture(t), command = "true";
for (let i = 0; i < 258; i++) {
const prepared = await f.runtime.prepare(f.input(command, f.current, "many-call-" + i));
await waitActor(prepared.spawn()); prepared.dispose();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export function createProtectedHostStageFixture(t, { registry: suppliedRegistry
const stagePath = join(root, "stage"); mkdirSync(stagePath, { mode: 0o700 });
const store = createSqliteRunStore(cwd), arenas = [], stages = new Set();
const pin = path => ({ path: realpathSync(path), sha256: createHash("sha256").update(readFileSync(path)).digest("hex") });
const config = { inputHelper: { ...pin("/usr/bin/true"), version: "stage-test-v1" }, executables: { node: pin(process.execPath) } };
const config = { inputHelper: { ...pin("/usr/bin/true"), version: "stage-test-v1" },
executables: { node: pin(process.execPath), true: pin("/usr/bin/true") } };
const supervisor = new RuntimeSupervisor(resolveConfig({ defaultWorkspaceCwd: cwd, sandbox: { restrictedCommands: config } }));
// The real native cases use the production Supervisor's enrolled Registry.
// Generic wrapper tests may supply their separate, initially unbound Registry
Expand Down
Loading