mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-24 18:19:05 +02:00
## Thinking Path > - Paperclip manages AI agents and their work. > - Pipelines was an unused experiment with its own UI, API, CLI, and automation. > - Keeping it leaves unsupported product surfaces and extra task-detail queries. > - Removing the experiment must preserve data that users already saved. > - This pull request removes the feature and keeps its database schema and migration history. > - Cases and Routines remain available as separate features. ## Linked Issues or Issue Description **What existing behavior does this improve?** Remove the unused Pipelines experiment across the application. **Current behavior** The experiment includes pipeline boards, stage settings, case automation, review queues, learnings, permissions, and CLI commands. Task detail also queries pipeline links. **Proposed behavior** Remove all these product surfaces and their implementation. Preserve existing tables, records, and documents. Ignore the retired boolean in older managed configurations so upgrades still start. **Breaking changes** Pipeline URLs, API endpoints, CLI commands, and `pipelines:write` are removed. Task detail no longer includes the old `linkedCases` field. The independent Cases API remains available. No database migration or cleanup is included. Related feature work: #7907, #9110, #10905, #11375, #13004, #14671, #16133. Related issues: #9663, #10310, #10312. This removal supersedes the feature; it does not implement those pending enhancements or delete their saved data. ## What Changed - Remove Pipeline UI pages, navigation, gates, clients, query keys, helpers, and unused design tokens. - Remove Pipeline REST routes, services, automation dispatch, task-detail queries, and OpenAPI discovery. - Remove the CLI commands, shared contracts, experimental flag, catalog entry, and permission defaults. - Remove feature-specific tests, tutorials, smoke scripts, maps, and Storybook examples. - Preserve database schema, migration history, and the reserved document key used by old records. - Keep Cases routes independent and cover obsolete stored and managed settings. ## Verification - Passed: `pnpm build` and `pnpm -r typecheck`. - Passed: targeted settings, managed configuration, sidebar, settings UI, routing, and feature-catalog tests. A first sidebar run caught a shared Workspaces icon import; the import was restored and the sidebar rerun passed. - Passed: 40 tests covering Cases routes, OpenAPI discovery, retained Pipeline table constraints, and schema snapshot drift. - Passed: token gates, map validation, generated API metadata check, Storybook catalog validation, and 28 map/catalog tests. - Passed: isolated local browser verification of navigation and Experimental settings with Pipelines absent; Cases and Routines still open normally. Live GET/POST requests to retired Pipeline endpoints return 404, and the retired setting is absent. CLI help has no Pipeline commands. - Passed: 22 access-service and agent-default-grant tests after restoring unrelated permission assertions flagged in review. - Local full run: `pnpm test:run` reported one failure in the unchanged workspace-runtime port-allocation test. That test passed in an isolated rerun. The long local run was stopped after full CI passed; it is not reported as a complete local pass. - Passed: all 56 checks on `def9181f3a4491d95fca1d38e9739f5b821aae3e` (2 conditional checks skipped), including build, typecheck, test shards, and browser suites. Eleven interrupted CI jobs reported “The operation was canceled”; their one-time retry passed. [CI run](https://github.com/paperclipai/paperclip/actions/runs/39255211006). - Greptile: 5/5 on the final commit, with the only review finding fixed and resolved. - Sandbox socket restrictions affected the first build/API test attempts. The same commands ran with local sockets available. ## Risks - Existing Pipeline clients must stop using the removed endpoints and commands. - Existing data remains in the database and backups, but the removed UI no longer displays it. - Historical boolean `enablePipelines` values are ignored. They cannot restore the feature. - Cases and Routines must retain their separate behavior. Targeted route tests cover this boundary. ## Model Used OpenAI Codex, GPT-6, with repository inspection, editing, shell execution, and browser verification. The exact deployed model ID and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
135 lines
5.2 KiB
TypeScript
135 lines
5.2 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { INSTANCE_FEATURE_CATALOG } from "@paperclipai/shared";
|
|
import {
|
|
applyCloudCatalogDefaults,
|
|
applyExperimentalSettingsPatch,
|
|
applyManagedExperimentalOverlay,
|
|
normalizeExperimentalSettings,
|
|
stripCloudCatalogDefaultEchoes,
|
|
} from "../services/instance-settings.js";
|
|
import type { ManagedInstanceConfig } from "../services/managed-config.js";
|
|
|
|
function managedConfig(features: ManagedInstanceConfig["features"] = {}): ManagedInstanceConfig {
|
|
return {
|
|
v: 1,
|
|
mode: "cloud",
|
|
catalogVersion: "test",
|
|
features,
|
|
plugins: { autoInstall: [] },
|
|
environments: [],
|
|
};
|
|
}
|
|
|
|
describe("applyCloudCatalogDefaults", () => {
|
|
it("pins the catalog so this rule has something to guard", () => {
|
|
// The rule exists for flags that default on for self-hosted and off for
|
|
// Cloud. If that set ever empties, the helper is dead code and should go.
|
|
const guarded = Object.entries(INSTANCE_FEATURE_CATALOG)
|
|
.filter(([, entry]) => entry.selfHostedDefault === true && entry.cloudDefault === false)
|
|
.map(([key]) => key);
|
|
expect(guarded).toContain("enableNativeRunner");
|
|
});
|
|
|
|
it("leaves self-hosted instances on the schema default", () => {
|
|
const experimental = applyCloudCatalogDefaults(normalizeExperimentalSettings({}), {}, null);
|
|
expect(experimental.enableNativeRunner).toBe(true);
|
|
});
|
|
|
|
it("re-asserts the Cloud default when the tenant row and the overlay omit the flag", () => {
|
|
const experimental = applyCloudCatalogDefaults(
|
|
normalizeExperimentalSettings({}),
|
|
{},
|
|
managedConfig(),
|
|
);
|
|
expect(experimental.enableNativeRunner).toBe(false);
|
|
// Flags with matching defaults are untouched.
|
|
expect(experimental.enableStreamlinedUi).toBe(true);
|
|
});
|
|
|
|
it("keeps an explicit tenant value", () => {
|
|
const raw = { enableNativeRunner: true };
|
|
const experimental = applyCloudCatalogDefaults(
|
|
normalizeExperimentalSettings(raw),
|
|
raw,
|
|
managedConfig(),
|
|
);
|
|
expect(experimental.enableNativeRunner).toBe(true);
|
|
});
|
|
|
|
it("lets a managed feature value win through the overlay", () => {
|
|
const config = managedConfig({ enableNativeRunner: true });
|
|
const { experimental } = applyManagedExperimentalOverlay(
|
|
applyCloudCatalogDefaults(normalizeExperimentalSettings({}), {}, config),
|
|
config,
|
|
);
|
|
expect(experimental.enableNativeRunner).toBe(true);
|
|
});
|
|
|
|
it("does not touch flags whose Cloud default is the enabled one", () => {
|
|
// enableOwnerInstanceAdmin defaults off for self-hosted and on for Cloud.
|
|
// That direction is resolved elsewhere; this helper must not flip it.
|
|
const experimental = applyCloudCatalogDefaults(
|
|
normalizeExperimentalSettings({}),
|
|
{},
|
|
managedConfig(),
|
|
);
|
|
expect(experimental.enableOwnerInstanceAdmin).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("stripCloudCatalogDefaultEchoes", () => {
|
|
/** What `updateExperimental` would persist for a given row and patch. */
|
|
function persisted(rawStored: unknown, patch: Record<string, unknown>, config: ManagedInstanceConfig | null) {
|
|
return stripCloudCatalogDefaultEchoes(
|
|
rawStored,
|
|
patch,
|
|
applyExperimentalSettingsPatch(rawStored, patch),
|
|
config,
|
|
) as Record<string, unknown>;
|
|
}
|
|
|
|
/** What a later read of that persisted row shows. */
|
|
function readBack(stored: Record<string, unknown>, config: ManagedInstanceConfig | null) {
|
|
return applyManagedExperimentalOverlay(
|
|
applyCloudCatalogDefaults(normalizeExperimentalSettings(stored), stored, config),
|
|
config,
|
|
).experimental;
|
|
}
|
|
|
|
it("does not persist the self-hosted default on Cloud during an unrelated write", () => {
|
|
const config = managedConfig();
|
|
const stored = persisted({}, { enableStatusCards: true }, config);
|
|
expect(stored.enableStatusCards).toBe(true);
|
|
expect("enableNativeRunner" in stored).toBe(false);
|
|
// The Cloud default still applies on the next read.
|
|
expect(readBack(stored, config).enableNativeRunner).toBe(false);
|
|
});
|
|
|
|
it("treats a full-GET echo of the Cloud default as no choice", () => {
|
|
const config = managedConfig();
|
|
const stored = persisted({}, { enableNativeRunner: false, enableStatusCards: true }, config);
|
|
expect("enableNativeRunner" in stored).toBe(false);
|
|
expect(readBack(stored, config).enableNativeRunner).toBe(false);
|
|
});
|
|
|
|
it("persists an explicit Cloud opt-in", () => {
|
|
const config = managedConfig();
|
|
const stored = persisted({}, { enableNativeRunner: true }, config);
|
|
expect(stored.enableNativeRunner).toBe(true);
|
|
expect(readBack(stored, config).enableNativeRunner).toBe(true);
|
|
});
|
|
|
|
it("keeps a stored tenant value across unrelated writes", () => {
|
|
const config = managedConfig();
|
|
const stored = persisted({ enableNativeRunner: true }, { enableStatusCards: true }, config);
|
|
expect(stored.enableNativeRunner).toBe(true);
|
|
expect(readBack(stored, config).enableNativeRunner).toBe(true);
|
|
});
|
|
|
|
it("leaves the whole normalized object in place for self-hosted rows", () => {
|
|
const stored = persisted({}, { enableStatusCards: true }, null);
|
|
expect(stored.enableNativeRunner).toBe(true);
|
|
expect(stored).toEqual(applyExperimentalSettingsPatch({}, { enableStatusCards: true }));
|
|
});
|
|
});
|