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
6 changes: 3 additions & 3 deletions docs/product-name-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ git grep -n -i -E 'kapi|ilchul' -- ':!package-lock.json' ':!node_modules'
| Storage/config | `.ilchul`, `~/.ilchul`, `.kapi` historical references, and `ILCHUL_*` environment variables appear in docs/tests/runtime setup. | storage/config namespace | `.ilchul` is active storage; `.kapi` is legacy evidence only and must not be deleted by this issue. |
| GitHub review integration | `kapi-agent`, `kapi-agent/review`, and `kapi-review` remain literal external integration names. | external integration | Keep literal names while the GitHub App and required checks use them. |
| Documentation/history | README, policy docs, references, and historical explanations still mention Kapi and Ilchul. | documentation/history | New public runtime examples should use `ilchul`; internal implementation examples may use `runctl` / `runtime`; historical and compatibility prose may keep product names with context. |
| Core/domain/application identifiers | The registry entry type now uses `WorkflowRegistryEntry`; the store interface/class now use `WorkflowStore` / `FileWorkflowStore`; the service class now uses `WorkflowService` with a temporary `KapiService` compatibility export. The local factory export is now `createLocalWorkflowService`. | known reusable-code leakage reduced by bounded slices | Follow-up slices should migrate compatibility imports, then remove the remaining service alias once safe. |
| Application service filenames | The service implementation and factory now use semantic filenames: `src/application/workflow-service.ts` and `src/adapters/workflow-service-factory.ts`; the primary exported service class is now `WorkflowService`. | internal semantic implementation | Keep imports on the semantic service paths; the temporary `KapiService` alias remains only for compatibility until follow-up test/import migration removes it. |
| Core/domain/application identifiers | The registry entry type now uses `WorkflowRegistryEntry`; the store interface/class now use `WorkflowStore` / `FileWorkflowStore`; the service class now uses `WorkflowService` with a temporary `KapiService` compatibility export. Most small test imports now use `WorkflowService`; the remaining compatibility imports are concentrated in the large `service-store` and `autoresearch-validation` test clusters. | known reusable-code leakage reduced by bounded slices | Follow-up slices should migrate the two remaining large test clusters, then remove the remaining service alias once safe. |
| Application service filenames | The service implementation and factory now use semantic filenames: `src/application/workflow-service.ts` and `src/adapters/workflow-service-factory.ts`; the primary exported service class is now `WorkflowService`. | internal semantic implementation | Keep imports on the semantic service paths; the temporary `KapiService` alias remains only for the two remaining large compatibility-import test clusters until follow-up migration removes it. |
| CLI worker/runtime helper filenames | Worker event, runtime observation, and GitHub issue context helpers now use semantic filenames: `src/cli/worker-events.ts`, `src/cli/worker-runtime.ts`, and `src/cli/github-issue-context.ts`. | internal semantic implementation | Keep imports on the semantic helper paths; remaining worker event payload names such as `kapi.worker.*` are external event contracts, not filenames. |

## Guard added in this slice
Expand All @@ -36,7 +36,7 @@ git grep -n -i -E 'kapi|ilchul' -- ':!package-lock.json' ':!node_modules'
- PR review state helpers use generic pull-request / agent-review names (`PullRequestReviewState`, `PullRequestReviewView`, `latestAgentReview`, `reviewCheckConclusion`).
- Registry entry types now use the semantic `WorkflowRegistryEntry` name across the domain, adapter, CLI, presentation, and tests.
- Store abstractions now use semantic `WorkflowStore` / `FileWorkflowStore` names across application ports, adapters, and runtime probe scripts; compatibility imports have been migrated and the temporary store alias has been removed.
- Service source surfaces now use semantic `WorkflowService` in the implementation, local factory, presentation layer, and runtime probe scripts, with a temporary `KapiService` export for compatibility imports.
- Service source surfaces now use semantic `WorkflowService` in the implementation, local factory, presentation layer, runtime probe scripts, and the first batch of small service tests, with a temporary `KapiService` export for the remaining large compatibility-import test clusters.

## Residual scan after service filename rename

Expand Down
16 changes: 8 additions & 8 deletions test/active-pointer-safety.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";

test("workflow state saves reject symlinked state files", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-state-symlink-safety-"));
const outside = path.join(workspace, "outside-state.json");
try {
const store = new FileWorkflowStore();
const service = new KapiService(store);
const service = new WorkflowService(store);
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "State symlink safety" });
const statePath = path.join(started.state.artifactRoot, "state.json");
await rm(statePath);
Expand All @@ -30,7 +30,7 @@ test("active pointer saves reject symlinked active pointers", async () => {
const outside = path.join(workspace, "outside-active.json");
try {
const store = new FileWorkflowStore();
const service = new KapiService(store);
const service = new WorkflowService(store);
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Active symlink safety" });
const activePath = path.join(workspace, ".ilchul", "active.json");
await rm(activePath);
Expand All @@ -51,7 +51,7 @@ test("workflow starts reject symlinked Kapi state directories", async () => {
await mkdir(outsideKapi);
await symlink(outsideKapi, path.join(workspace, ".ilchul"));

const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
await assert.rejects(
service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Ancestor symlink safety" }),
/state directories must not be symbolic links: \.ilchul/i,
Expand All @@ -65,7 +65,7 @@ test("active pointer reads reject symlinked active pointers", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-active-read-symlink-safety-"));
const outside = path.join(workspace, "outside-active.json");
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Active read symlink safety" });
const activePath = path.join(workspace, ".ilchul", "active.json");
await rm(activePath);
Expand All @@ -82,7 +82,7 @@ test("active pointer reads reject symlinked workflow state files", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-state-read-symlink-safety-"));
const outside = path.join(workspace, "outside-state.json");
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "State read symlink safety" });
const statePath = path.join(started.state.artifactRoot, "state.json");
await rm(statePath);
Expand All @@ -98,7 +98,7 @@ test("active pointer reads reject symlinked workflow state files", async () => {
test("active pointers with mismatched embedded workspace state are cleared instead of loaded", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-active-pointer-mismatch-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Mismatched embedded workspace" });
const statePath = path.join(started.state.artifactRoot, "state.json");
await writeFile(
Expand Down Expand Up @@ -133,7 +133,7 @@ test("stale active pointers outside the workspace are cleared instead of loaded"
"utf8",
);

const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
assert.equal(await service.getActiveStatus(workspace), undefined);
await assert.rejects(() => readFile(path.join(workspace, ".ilchul", "active.json"), "utf8"), /ENOENT/);
} finally {
Expand Down
10 changes: 5 additions & 5 deletions test/active-replacement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";
import { FixedClock } from "./support/fixtures.js";

test("starting a new workflow requires explicit replacement when another workflow is active", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-replace-"));
try {
const service = new KapiService(new FileWorkflowStore(), new FixedClock());
const service = new WorkflowService(new FileWorkflowStore(), new FixedClock());
const plan = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Plan the change" });

await assert.rejects(
Expand All @@ -34,7 +34,7 @@ test("starting a new workflow requires explicit replacement when another workflo
test("explicit slugs reject detached workflow history overwrite", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-explicit-slug-overwrite-"));
try {
const service = new KapiService(new FileWorkflowStore(), new FixedClock());
const service = new WorkflowService(new FileWorkflowStore(), new FixedClock());
const first = await service.startWorkflow({ workspace, workflowId: "kapi-deep-interview", task: "Original explicit run", slug: "stable-history" });
await service.clear(workspace, "detach first run");

Expand All @@ -54,7 +54,7 @@ test("explicit slugs reject detached workflow history overwrite", async () => {
test("generated slugs avoid overwriting detached workflow history", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-generated-slug-unique-"));
try {
const service = new KapiService(new FileWorkflowStore(), new FixedClock());
const service = new WorkflowService(new FileWorkflowStore(), new FixedClock());
const first = await service.startWorkflow({ workspace, workflowId: "kapi-deep-interview", task: "Repeatable task" });
await service.clear(workspace, "detach first run");

Expand All @@ -74,7 +74,7 @@ test("targeted clear detaches only when the selected recorded workflow is active
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-target-clear-"));
try {
const store = new FileWorkflowStore();
const service = new KapiService(store, new FixedClock());
const service = new WorkflowService(store, new FixedClock());
const stale = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Stale blocked plan", slug: "stale-plan" });
await store.clearActive(workspace);
const active = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Current execution", slug: "current-exec" });
Expand Down
4 changes: 2 additions & 2 deletions test/artifact-empty-name.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";

test("artifact operations reject blank artifact names with a clear error", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-empty-artifact-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Reject blank artifact names" });

await assert.rejects(
Expand Down
4 changes: 2 additions & 2 deletions test/artifact-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";
import { formatArtifactList } from "../src/presentation/messages.js";

test("service lists tracked artifacts for the active workflow", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-status-artifacts-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
await service.startWorkflow({ workspace, workflowId: "kapi-deep-interview", task: "Interview" });
await service.writeArtifact({ workspace, artifactName: "notes.md", content: "\n## Optional note\n" });
await service.writeArtifact({ workspace, artifactName: "notes.md", content: "\n## Required note\n", required: true });
Expand Down
6 changes: 3 additions & 3 deletions test/artifact-overwrite.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";
import { FixedClock } from "./support/fixtures.js";

test("artifact writes can explicitly overwrite or append workflow-scoped content", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-status-artifact-overwrite-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Plan artifact overwrite behavior" });

await service.writeArtifact({ workspace, artifactName: "IMPLEMENTATION_PLAN.md", content: "first draft", append: false });
Expand Down Expand Up @@ -38,7 +38,7 @@ test("artifact writes can explicitly overwrite or append workflow-scoped content
test("terminal workflow completion syncs authored artifact headers without prepending decision logs", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-authored-artifact-sync-"));
try {
const service = new KapiService(new FileWorkflowStore(), new FixedClock("2026-05-09T00:00"));
const service = new WorkflowService(new FileWorkflowStore(), new FixedClock("2026-05-09T00:00"));
await service.startWorkflow({ workspace, workflowId: "kapi-deep-interview", task: "Clarify browser bridge" });
const handoffReadyBody = `# Decision Report

Expand Down
6 changes: 3 additions & 3 deletions test/artifact-path-safety-write.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";

test("artifact operations reject symlinked files instead of following them outside the artifact root", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-artifact-symlink-safety-"));
const outside = path.join(workspace, "outside.md");
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Artifact symlink safety", slug: "artifact-symlink-safety" });
const planPath = path.join(started.state.artifactRoot, "IMPLEMENTATION_PLAN.md");
const verifyPath = path.join(started.state.artifactRoot, "verify.md");
Expand All @@ -35,7 +35,7 @@ test("artifact operations reject symlinked files instead of following them outsi
test("artifact writes reject path separators and preserve active workflow state", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-status-artifact-write-safety-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Artifact path safety", slug: "artifact-safety" });

await assert.rejects(
Expand Down
4 changes: 2 additions & 2 deletions test/autoresearch-loop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import os from "node:os";
import path from "node:path";
import test from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";
import { parseAutoresearchContract, parseAutoresearchIdeas, runAutoresearchLoop, scanAutoresearchAntiGaming, serializeAutoresearchLedger, type AutoresearchCommandResult, type AutoresearchLoopEntry } from "../src/application/autoresearch-loop.js";
function runner(outputs: Record<string, AutoresearchCommandResult | AutoresearchCommandResult[]>) {
const calls: string[] = [];
Expand Down Expand Up @@ -100,7 +100,7 @@ test("autoresearch anti-gaming scan forces discard before keep decisions", async
test("serialized loop output stays valid under Kapi autoresearch ledger validation", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-autoresearch-loop-ledger-"));
try {
const service = new KapiService(new FileWorkflowStore());
const service = new WorkflowService(new FileWorkflowStore());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-autoresearch", task: "Validate loop ledger", slug: "loop-ledger" });
const benchmarkContent = "#!/usr/bin/env bash\nscore=1\nprintf 'METRIC score=%s\\n' \"$score\"\n";
const fx = runner({ "./benchmark.sh": [ok(5), ok(8)], "try-a": { exitCode: 0, stdout: "done" }, checks: { exitCode: 0, stdout: "ok" } });
Expand Down
4 changes: 2 additions & 2 deletions test/context-artifact-persistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import path from "node:path";
import { test } from "node:test";
import { FileWorkflowStore } from "../src/adapters/file-store.js";
import { FileProjectContextProvider } from "../src/adapters/project-context.js";
import { KapiService } from "../src/application/workflow-service.js";
import { WorkflowService } from "../src/application/workflow-service.js";

test("context-aware durable modes include project context in the prompt and keep verify.md as evidence ledger", async () => {
const workspace = await mkdtemp(path.join(os.tmpdir(), "kapi-context-artifact-"));
Expand All @@ -15,7 +15,7 @@ test("context-aware durable modes include project context in the prompt and keep
await mkdir(path.join(workspace, "src"), { recursive: true });
await writeFile(path.join(workspace, "src", "feature.ts"), "export const feature = true;\n", "utf8");

const service = new KapiService(new FileWorkflowStore(), undefined, new FileProjectContextProvider());
const service = new WorkflowService(new FileWorkflowStore(), undefined, new FileProjectContextProvider());
const started = await service.startWorkflow({ workspace, workflowId: "kapi-ralph", task: "Plan with brownfield context", slug: "brownfield-plan" });
const verify = (await service.readArtifact({ workspace, artifactName: "verify.md" }))?.content ?? "";
assert.match(verify, /"mode": "kapi-ralph"/);
Expand Down
Loading
Loading