Conversation
Introduces doc/AGENT-GIT-WORKFLOW.md defining branch naming, commit message format, PR requirements, periodic review cadence, and rollback runbook for all agent-authored commits to the Paperclip repo. Updates AGENTS.md to reference the new protocol as a required read and adds a summary section (§5) with the key rules inline. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…in support (ANGA-184) - Add `AdapterFailureCategory` union type with 12 provider-agnostic error codes - Add `AdapterFallbackEntry` interface with `triggerOn`, `adapterConfig`, `maxAttempts` fields - Export both types from @paperclipai/adapter-utils - Add `categorizeAdapterError()` in server/src/services/adapter-failure-taxonomy.ts mapping legacy adapter-specific codes (claude_auth_required, etc.) to canonical categories - Implement adapter fallback chain in heartbeat orchestrator: on primary failure, iterate adapterFallbackChain entries, execute matching fallback adapter without session state inheritance, replace result on success - Add 9 unit tests for error taxonomy and fallback trigger logic Co-Authored-By: Paperclip <noreply@paperclip.ing>
…rror code (ANGA-205) - Log adapter.fallback run events for each fallback attempt (start, success, failure, exception) - Emit all_adapters_exhausted errorCode + human-readable message when entire chain fails - Surfaces clearly in run transcript which adapters were tried and why Co-Authored-By: Paperclip <noreply@paperclip.ing>
…FOUND crash On Windows, passing an absolute path as a string to fork() caused Node to resolve the 'file:' prefix against cwd, corrupting the path. Now normalizeForkEntrypoint() returns a URL object on win32, which the ESM loader handles correctly. Closes ANGA-269 Co-Authored-By: Paperclip <noreply@paperclip.ing>
Merges feature/anga-269-fix-plugin-worker-fork-url: fixes MODULE_NOT_FOUND crash when enabling plugins on Windows. normalizeForkEntrypoint() now returns a URL object (not .href string) so Node ESM loader receives a proper file:// URL. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ck chain (ANGA-184) - Add AdapterFailureCategory union type (12 provider-agnostic error codes) - Add AdapterFallbackEntry interface with triggerOn/maxAttempts - Extract categorizeAdapterError() to adapter-failure-taxonomy.ts module - Implement fallback chain execution loop in heartbeat orchestrator - Agents without adapterFallbackChain behave exactly as before - Fallback adapters do not inherit primary adapter session state - 9 unit tests covering classification, legacy code mapping, and non-Claude fallback paths Also includes ANGA-205: fallback run events and all_adapters_exhausted error code. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Reviewed and approved by CTO. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…taxonomy (#8) Reviewed and approved by CTO. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Dev Agent — Products cannot checkout ANGA-309 (Windows ESM fix, blocks ANGA-86 dogfood) due to two stale CTO queued runs: - 3c902cb1-01fc-4e80-a523-3c64989046e0 - 5482ff8e-039a-4fbf-976f-c312903466de Same pattern as CTO's critical signal. Requests these two runs be added to approval be3efbad or cancelled separately. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Issues created via the Telegram ingest route now have assigneeAgentId set to TELEGRAM_ASSIGNEE_AGENT_ID (defaults to CTO agent) and trigger an immediate heartbeat wakeup so the CTO is notified on creation. Closes ANGA-240 Co-Authored-By: Paperclip <noreply@paperclip.ing>
…p (ANGA-240) The initial commit added the telegram route handler and event bridge service files but did not wire them into the Express app or startup sequence, so no Telegram-created issues would have been auto-assigned. - app.ts: import and register telegramRoutes(db) on the /api router - index.ts: import and call startTelegramEventBridge() at startup Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ent subprocess (ANGA-329)
runChildProcess merges { ...process.env, ...env }, so when authToken is
null (JWT secret missing), PAPERCLIP_API_KEY is absent from opts.env and
the server process's board-user token leaks into the agent child process.
The agent then authenticates as local-board: checkout does not set
checkoutRunId, and comments appear as board user.
Two fixes:
- claude-local execute.ts: when no explicit key is configured, always
write PAPERCLIP_API_KEY into env (authToken when available, empty
string otherwise) so process.env inheritance is blocked.
- heartbeat.ts: throw immediately when supportsLocalAgentJwt is true
but authToken is null, instead of logging a warning and continuing.
This is a belt-and-suspenders guard ensuring agents never launch
without a valid JWT.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ade (ANGA-335)
Adds a new endpoint that allows manager agents and board users to trigger
an on-demand heartbeat for a direct report, enabling cascaded heartbeat
checks from CEO/Operations Lead through the org hierarchy.
- Caller must be in the target agent's chainOfCommand or a board user
- Non-manager agent callers receive 403
- Returns 202 Accepted with the run object (or {status:"skipped"} if throttled)
- Logs heartbeat.triggered activity to the audit log
- Existing /heartbeat/invoke (self-only) is unchanged
Co-Authored-By: Paperclip <noreply@paperclip.ing>
chore: add cowork-inbox/ to .gitignore (ANGA-369)
… VCS) Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add docker-compose.observability.yml: Loki, Prometheus, Grafana Tempo, Grafana - Add docker/observability/ config: Loki, Prometheus, Tempo YAML + Grafana provisioning (datasources + dashboards) with linked datasources (Loki→Tempo) - Add server/src/observability/metrics.ts: prom-client counters/histograms/gauges for heartbeat runs, duration, token usage, HTTP requests, budget utilization - Add server/src/observability/tracing.ts: optional OTel SDK init via PAPERCLIP_OTEL_ENDPOINT env var (dynamic import, non-fatal on failure) - Add server/src/routes/metrics.ts: /metrics endpoint (enabled via env flag) - Wire metricsRoutes() into app.ts and initTracing() into server startup - Add pino-loki as optional third transport (enabled via PAPERCLIP_LOKI_URL) - Document all new env vars in .env.example - Grafana dashboard: paperclip-overview.json with token usage, heartbeat rates, error rates, API latency (p95), active/stalled runs, log panel All observability is opt-in via environment variables; existing deployments are unaffected. Run with: docker compose -f docker-compose.yml -f docker-compose.observability.yml up Closes ANGA-384 Co-Authored-By: Paperclip <noreply@paperclip.ing>
Paperclip server runs on host port 3100. The previous default mapped Loki's container port to the same host port, causing a conflict when running both the app and observability stack on the same host. Change default LOKI_PORT from 3100 to 3101 (internal container port stays 3100; inter-service comms via container network are unaffected). Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ssign feat(telegram): auto-assign Telegram issues to CTO and add event bridge (ANGA-240)
Implements ANGA-235. Adds reapStaleExecutionRunLocks() to heartbeatService: - Queries issues whose executionRunId references a terminal run (skipped/ succeeded/failed/cancelled/timed_out) and clears both executionRunId and checkoutRunId so the issue can be checked out again without manual board intervention. - Called at server startup (after reapOrphanedRuns) and on every heartbeat-scheduler tick, giving ≤60 s recovery latency. - Also fixes releaseIssueExecutionAndPromote to clear checkoutRunId (it previously only cleared executionRunId), eliminating a partial-lock survivor that could block re-checkout. Tests: three new cases in heartbeat-process-recovery.test.ts covering terminal-run reap, live-run non-reap, and no-op empty state. Updates an existing assertion that now correctly expects checkoutRunId=null after the second process-loss retry exhaustion path. Closes ANGA-235 Co-Authored-By: Paperclip <noreply@paperclip.ing>
Addresses Greptile P2: reapStaleExecutionRunLocks was unconditionally setting checkoutRunId to NULL whenever executionRunId referenced a terminal run, without verifying that checkoutRunId belonged to the same run. Use a CASE WHEN expression so checkoutRunId is cleared only when it matches the terminal run ID; a checkoutRunId belonging to a different run is left intact. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…onRunLocks Two P2 issues from code review: 1. TERMINAL_RUN_STATUSES was defined inside the function on every call; moved to module scope as a shared constant. 2. N+1 update loop (one UPDATE per stale row) replaced with a single bulk UPDATE using OR predicates. The per-row (id, executionRunId) guard is preserved — no stale row can clear a lock that was already taken by a new run. The CASE on checkoutRunId uses the same logic: clear only when it still references the terminal run being reaped. Co-Authored-By: Paperclip <noreply@paperclip.ing>
'skipped' is a WakeupRequestStatus, not a HeartbeatRunStatus. The valid heartbeatRun terminal statuses are: succeeded, failed, cancelled, timed_out. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…y (ANGA-165) Before spawning an adapter child process, check os.freemem(). If free memory is below 2 GB (LOW_MEMORY_THRESHOLD_BYTES), revert the run to "queued" status, log a system event with MB figures, and return early. The run will be retried by resumeQueuedRuns() on the next scheduler tick, preventing OOM kills that manifest as spurious process_lost crashes. When reapOrphanedRuns() finalises a process_lost run, include os.freemem() and os.totalmem() in the run event payload so operators can correlate crashes with memory pressure in the logs. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…GA-437) Instruments the heartbeat runner with actual call sites so Prometheus receives real data, not zero-valued series. - heartbeat_runs_active gauge: inc on run claim, dec in finally (covers all exit paths including success, failure, and setup errors) - heartbeat_runs_total counter: inc with agent_id + status on all completion paths (success, inner catch, outer setup failure catch) - heartbeat_duration_seconds histogram: observe on all completion paths using wall-clock from run.startedAt - tokens_used_total counter: inc input/output tokens on success path when normalizedUsage is available All instrumentation is gated behind isMetricsEnabled() cached once per run as metricsOn to avoid repeated env-var reads. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…NGA-438)
- Add `job: "paperclip"` label (required for Loki query {job="paperclip"})
- Add `agentId` label from PAPERCLIP_AGENT_ID env var when present
- Promote `runId` log field to Loki label via propsToLabels
- Transport remains opt-in via PAPERCLIP_LOKI_URL, silenceErrors ensures
non-fatal behavior when Loki is unavailable
- Install pino-loki@2.6.0 and update pnpm-lock.yaml
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace mutable lokiLabels object mutation with immutable object spread. Same behavior, cleaner intent. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Resolve merge conflicts between upstream sync branch and fork's master. Keep both upstream changes (feedbackService, telemetry tests) and fork-specific additions (Telegram event bridge, reapStaleExecutionRunLocks tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chore: sync with upstream master (2026-04-04)
…beats (ANGA-521) Adds a new `remote_trigger` adapter type that suppresses native heartbeat scheduling, allowing external schedulers (e.g. CoWork RemoteTriggers) to drive agent heartbeats without double-firing. - `AGENT_ADAPTER_TYPES` in shared/constants.ts now includes "remote_trigger" - `tickTimers` in heartbeat.ts skips agents with adapterType = "remote_trigger" - No DB migration needed (adapter_type is plain text column) - All zod validators that use `z.enum(AGENT_ADAPTER_TYPES)` automatically accept the new value (PATCH /agents/:id, access checks, etc.) Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(adapters): add unit tests for stream, trust, and openclaw format-event 36 tests across 3 small files: - cursor-local/src/shared/stream.test.ts (11 tests): normalizeCursorStreamLine — empty/whitespace returns null stream + empty line, plain JSON passthrough, stdout:/stderr: prefix stripping, case-insensitive prefix matching, equals sign variant, whitespace trimming, non-JSON content after prefix is not stripped (plain text), array JSON matched - cursor-local/src/shared/trust.test.ts (9 tests): hasCursorTrustBypassArg — --trust, --yolo, -f, --trust=value all return true; false for empty array, no trust flags, similar but non-matching flags, partial -f within longer flag - openclaw-gateway/src/cli/format-event.test.ts (7 tests): printOpenClawGatewayStreamEvent — empty/whitespace no-op, raw passthrough in non-debug, openclaw-gateway:event and openclaw-gateway prefix coloring in debug, other debug output, whitespace trimming Co-Authored-By: Paperclip <noreply@paperclip.ing> * test(plugins/sdk): add 32 unit tests for createPluginBundlerPresets Covers defaults, custom config overrides, esbuild details (format, platform, bundle flags, externals), and rollup details (format, entryFileNames, externals). Adds vitest.config.ts and test script to the plugin-sdk package, and registers the package in the root workspace vitest config so CI picks it up. Co-Authored-By: Paperclip <noreply@paperclip.ing> --------- Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…d JSON-RPC protocol (#142) - packages/mcp-server/src/format.test.ts: 17 tests for formatTextResponse (string passthrough, JSON.stringify for objects/primitives) and formatErrorResponse (PaperclipApiError fields, plain Error message, non-Error String() coercion) - packages/plugins/sdk/src/protocol.test.ts: 81 tests for createRequest (auto-id, explicit id, counter reset), createSuccessResponse, createErrorResponse (with/without data, null id), createNotification, all five isJsonRpc* type guards, serializeMessage round-trip, parseMessage (happy path + parse errors), JsonRpcParseError, JsonRpcCallError - Adds vitest.config.ts and test/vitest devDep to plugin-sdk package - Registers packages/mcp-server and packages/plugins/sdk in root vitest.config.ts workspace projects Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
…143) - cli/src/utils/net.test.ts (3 tests): checkPort — free port reports available, occupied port reports unavailable with error message, uses real node:net sockets for integration fidelity - cli/src/utils/path-resolver.test.ts (7 tests): resolveRuntimeLikePath — absolute paths pass through, ~ prefix expands to home, config-sibling file preferred over workspace candidates, falls back to first candidate when nothing exists on disk - cli/src/config/secrets-key.test.ts (11 tests): ensureLocalSecretsKeyFile — skipped_provider, skipped_env (set/empty/whitespace), existing file (returns path, no overwrite), created (new file with random base64 key, recursive mkdir, env override via PAPERCLIP_SECRETS_MASTER_KEY_FILE); uses beforeEach env stubbing to prevent outer-env PAPERCLIP_* vars from polluting test isolation Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…ry (#147) - cli/src/config/data-dir.test.ts (14 tests): applyDataDirOverride — returns null when dataDir is absent/empty/whitespace; sets PAPERCLIP_HOME to resolved path when dataDir is provided; with hasConfigOption=true sets PAPERCLIP_CONFIG unless already set or options.config is present; with hasContextOption=true sets PAPERCLIP_CONTEXT unless already set; uses beforeEach/afterEach env stubbing for full isolation - cli/src/adapters/registry.test.ts (8 tests): getCLIAdapter — each named adapter type (claude_local, cursor, codex_local, opencode_local, gemini_local, openclaw_gateway) returns adapter with matching type field; unknown type falls back to process adapter; all known adapters expose a formatStdoutEvent function Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…ctions, and config paths (#144) - server/src/services/agent-permissions.test.ts (16 tests): defaultPermissionsForRole — ceo gets canCreateAgents=true, all other roles false; normalizeAgentPermissions — stored boolean values override role defaults, non-boolean/null/undefined/array/string stored values fall back to role defaults - server/src/services/default-agent-instructions.test.ts (6 tests): resolveDefaultAgentInstructionsBundleRole — 'ceo' → 'ceo', all other roles including '' → 'default', case-sensitive - server/src/paths.test.ts (10 tests): resolvePaperclipConfigPath — explicit override (absolute passthrough), PAPERCLIP_CONFIG env, ancestor .paperclip/config.json search via cwd mock, default fallback; resolvePaperclipEnvPath — .env sibling of config, with/without override Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…apters (#148) - cursor-models: 39 tests for parseCursorModelsOutput (JSON array/object/string, plain text 'available models:' lines, bullet lists, dedup, stderr fallback, isLikelyModelId validation) and listCursorModels (injected runner, cache, fallback, merge behavior via setCursorModelsRunnerForTests test hooks) - codex-models: 24 tests for listCodexModels (no-key fallback, successful fetch with bearer auth, model merging, dedup, caching, network errors, non-OK HTTP status) and resetCodexModelsCacheForTests Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…150) * test(cli,server): 62 unit tests for llm-check and dev-server-status - llm-check: 22 tests covering no-LLM pass, provider-without-key pass, claude provider (200/400 pass, 401 fail, 503 warn, network error warn), openai provider (200 pass, 401 fail, 429 warn, network error warn), correct API endpoint called for each provider (fetch mocked) - dev-server-status: 40 tests covering readPersistedDevServerStatus (null when no env var/empty/missing file, parses complete object, malformed JSON, caps changedPathsSample at 5, filters non-strings, derives dirty from counts, normalizes timestamps, uses sample length as count fallback), and toDevServerHealthStatus (reason=null/backend_changes/pending_migrations/both, restartRequired from dirty flag or changes, waitingForIdle logic, passthrough fields) Co-Authored-By: Paperclip <noreply@paperclip.ing> * test(server): 29 unit tests for plugin-stream-bus - createPluginStreamBus: subscribe/publish delivers events to listeners, defaults eventType to 'message', passes explicit eventType through, delivers to multiple subscribers of same channel - Key isolation: different channel/company/plugin combinations do not receive each other's events, correct subscriber receives targeted publish - Unsubscribe: returns function, stops delivery after call, only removes specific subscriber, no throw on double-unsubscribe - Edge cases: publish with no subscribers doesn't throw, events delivered in order, all StreamEventType variants handled Co-Authored-By: Paperclip <noreply@paperclip.ing> --------- Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
…tation guard (#151) - queueIssueAssignmentWakeup: covers no-op conditions (null assignee, backlog status), correct wakeup arguments, error swallowing and rethrow-on-error - boardMutationGuard: covers safe method pass-through, non-board actors, trusted sources (local_implicit, board_key), origin/referer/host/ x-forwarded-host/PAPERCLIP_PUBLIC_URL trust checks, and 403 on untrusted origin Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing> Co-authored-by: Paperclip <noreply@paperclip.ing>
* test(cli): 36 unit tests for port-check, log-check, and config-check
- port-check: 10 tests verifying portCheck returns pass/warn based on checkPort
result, includes port number in messages, respects custom error string from
checkPort, and sets correct canRepair/repairHint fields (checkPort mocked)
- log-check: 13 tests verifying logCheck creates missing directories, returns
pass for writable dirs, returns fail for non-writable dirs, sets correct name
and message fields (tests use real temp dirs with chmod)
- config-check: 13 tests verifying configCheck returns fail when config missing,
pass for valid config, fail with error message for invalid config, and sets
correct repairHint in each case (config/store.js mocked)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(cli): 36 more unit tests for agent-jwt-secret-check and deployment-auth-check
- agent-jwt-secret-check: 13 tests covering pass (secret in env), warn (secret
in .env file but not loaded into environment), and fail (missing from both)
cases — verify repair function calls ensureAgentJwtSecret, env file path
appears in message and repairHint (config/env.js mocked)
- deployment-auth-check: 23 tests covering local_trusted/loopback pass,
non-loopback fail, inferred bind from localhost/127.0.0.1, authenticated
mode with/without BETTER_AUTH_SECRET, explicit baseUrlMode validation,
public exposure https/http/invalid URL handling, JWT secret fallback
(env vars stubbed with vi.stubEnv)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(cli): 44 more unit tests for secrets-check and storage-check
- secrets-check: 24 tests covering unsupported provider fail, env key valid
(64-char hex, 32-byte base64, raw 32-char string) and invalid, key file
missing (warn + canRepair + repair creates file), key file present with
valid/invalid content, and strictMode=false postgres downgrade behavior
- storage-check: 20 tests covering local_disk writable pass, missing directory
creation, non-writable directory fail (chmod), and S3 warn/fail for empty
bucket or region
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(cli): 18 unit tests for database-check
- postgres mode: fail with no connection string (DB not called), pass on
successful execute(), fail with error message on connection rejection
- embedded-postgres mode: pass when dataDir is writable, creates missing
dataDir, message includes path and port number
- unknown mode: fail with mode name in message
(@paperclipai/db mocked to avoid real DB connections)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(tests): use valid enum values in deployment-auth-check tests
Replace invalid `"local_network"` → `"authenticated"` and
`"automatic"` → `"auto"` to match actual DeploymentMode and
AuthBaseUrlMode union types defined in the schema.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(tests): fix JWT fallback test for deployment-auth-check
The fallback test used vi.stubEnv("BETTER_AUTH_SECRET", "") inside a
describe block whose beforeEach already set it to "my-auth-secret".
Setting it to "" doesn't trigger the ?? fallback (only undefined does).
Extract test to its own describe, explicitly delete BETTER_AUTH_SECRET
before the test, and restore it in afterEach.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
…d selectCurrentRuntimeServiceRows - worktree-config.test.ts: tests the pure applyRuntimePortSelectionToConfig function covering server port update, db port update, auth URL port rewrite, allowServerPortWrite/allowDatabasePortWrite flags, and no-op paths - workspace-runtime-read-model.test.ts: tests the pure selectCurrentRuntimeServiceRows function covering reuseKey dedup, composite key dedup, null field handling, and insertion-order preservation Co-Authored-By: Paperclip <noreply@paperclip.ing>
Tests readPaperclipEnvEntries, writePaperclipEnvEntries, mergePaperclipEnvEntries, and readAgentJwtSecretFromEnvFile using real temp directories to verify file creation, parsing, merging, quoting, and round-trip correctness. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Covers createPluginEventBus: basic emit delivery, exact/wildcard pattern matching, EventFilter (companyId, projectId, agentId, AND logic), error isolation (handler throws collected in errors array without blocking other plugins), clearPlugin/forPlugin.clear, subscriptionCount, and scoped plugin.emit namespace enforcement. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Tests cover: createVersion (scheme, field presence, SHA-256 correctness, random IV uniqueness), resolveVersion round-trips (ASCII, special chars, unicode, empty, large), error handling (missing/wrong scheme, missing iv, tampered ciphertext), and master key format acceptance (hex, base64, invalid key rejection) via PAPERCLIP_SECRETS_MASTER_KEY env injection. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…nt helper TypeScript TS2783 error: 'eventType' was specified explicitly and then also spread from the overrides object. Remove the explicit assignment and let the spread handle it — eventType is required in overrides anyway. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Covers createPluginToolRegistry factory: - parseNamespacedName and buildNamespacedName (edge cases, separator position, invalid inputs) - registerPlugin: idempotent re-registration, pluginDbId fallback, empty tools, multi-plugin coexistence - unregisterPlugin: full removal, no-op on unknown plugin, other plugins unaffected - getTool / getToolByPlugin: found/not-found, removed after unregister - listTools: all tools, filter by pluginId, empty registry/plugin - toolCount: total, per-plugin, unknown plugin, decrements on unregister - executeTool: invalid name, not registered, worker not running, no workerManager, successful dispatch with correct call params Co-Authored-By: Paperclip <noreply@paperclip.ing>
Covers the exported toIssueWorkProduct pure function in work-products service: - Maps all required and optional fields from a fully-populated row - Coerces null/undefined optional fields to null (not undefined) - Verifies type/reviewState/healthStatus string passthrough - Preserves Date object references for createdAt/updatedAt - Verifies metadata object reference and complete key set Co-Authored-By: Paperclip <noreply@paperclip.ing>
Two test expectations were wrong: 1. "does not rewrite non-loopback publicBaseUrl" — was passing serverPort=3200 vs config's 3100, causing changed=true from the server-port update rather than the URL logic. Fixed to pass serverPort=3100 to isolate URL behavior. 2. "does not mark changed when loopback URL port already matches" — URL.toString() normalizes "http://127.0.0.1:3100" → "http://127.0.0.1:3100/" (trailing slash), making the rewritten string differ from the input even at the same port. Fixed to use the already-normalized URL form with trailing slash as input. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Covers getSecretProvider and listSecretProviders: - getSecretProvider returns the correct module for each of the 4 provider IDs - getSecretProvider throws for unknown provider IDs - Stub providers (aws, gcp, vault) have requiresExternalRef=true and throw on createVersion/resolveVersion - listSecretProviders returns all 4 descriptors with non-empty labels - Each listed descriptor id matches back through getSecretProvider Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(server): add unit tests for quota-windows and startup-banner
- fetchAllQuotaWindows: covers empty/filtered adapters, successful
results, error handling, and provider slug mapping (claude_local →
anthropic, codex_local → openai, unknown → type as-is)
- printStartupBanner: covers console output, embedded/external DB
details, connection-string redaction, port display, host
normalization, and JWT secret status
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(server): add unit tests for plugin-host-service-cleanup
Covers createPluginHostServiceCleanup: lifecycle registration,
handleWorkerEvent (crashed invokes, restarted skips), lifecycle event
routing (worker_stopped / plugin.unloaded with map removal), disposeAll
(all called + map cleared), and teardown (off called, events stop).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(server): add unit tests for error-handler middleware
Covers errorHandler: HttpError client errors (status, message, details,
no-details), server errors (500/503), ZodError (400 + details), and
generic Error/non-Error thrown values (all → 500).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(server): add unit tests for live-events pub/sub
Covers publishLiveEvent (company delivery, cross-company isolation,
global non-delivery, multi-subscriber), returned event shape (companyId,
type, numeric id, incremented ids, createdAt ISO, payload/defaults),
publishGlobalLiveEvent (delivery, company isolation, companyId='*'),
and unsubscribe (stops delivery, only removes specific listener).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* test(server): add unit tests for dev-runner-worktree helpers
Tests resolveWorktreeEnvFilePath (pure path computation),
isLinkedGitWorktreeCheckout (filesystem .git file detection),
and bootstrapDevRunnerWorktreeEnv (env file loading/parsing)
using real temp dirs for filesystem assertions.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(test): use valid LiveEventType values in live-events tests
Replace "issue.created", "issue.updated", "agent.updated" with
valid event types from the LiveEventType union
("heartbeat.run.queued", "activity.logged", "agent.status").
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(test): use PAPERCLIP_CONFIG env var instead of vi.mock for paths in startup-banner test
The vi.mock("./paths.js", ...) module mock was unreliable in CI ESM mode.
paths.ts checks process.env.PAPERCLIP_CONFIG first, so stubbing that env var
in beforeEach gives the same predictable config path without module mocking.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
---------
Co-authored-by: Dev Agent — Platform <dev-agent-platform@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Merging test/server-pure-funcs-153: unit tests for worktree-config and workspace-runtime-read-model
Merging test/server-misc-154: unit tests for plugin-tool-registry
Incorporates 15 upstream commits from paperclipai/paperclip: - Generalize sandbox provider core for plugin-only providers (paperclipai#4449) - Stabilize serialized server route tests (paperclipai#4448) - Polish markdown external link wrapping (paperclipai#4447) - Gate stale-run watchdog decisions by board access (paperclipai#4446) - Cancel stale retries when issue ownership changes (paperclipai#4445) - Normalize escaped multiline issue and approval text (paperclipai#4444) - Add runtime lifecycle recovery and live issue visibility (paperclipai#4419) - Stabilize tests and local maintenance assets (paperclipai#4423) - Add sandbox environment support (paperclipai#4415) - Harden create-agent skill governance (paperclipai#4422) - Polish issue composer and long document display (paperclipai#4420) - Improve transient recovery and Codex model refresh (paperclipai#4383) - Refine markdown issue reference rendering (paperclipai#4382) - Improve issue thread review flow (paperclipai#4381) - Speed up company skill detail loading (paperclipai#4380) Conflicts resolved by accepting upstream for all conflicting files. Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Remove duplicate Status Quick Guide (was listed twice in Step 8) - Remove Manual CLI note from Auth (not relevant to heartbeat agents) - Compress Issue Dependencies: drop JSON code examples, keep prose - Compress Board Approval: inline fields instead of full JSON block - Merge four pointer sections (Niche Workflows, Company Skills, Routines, Issue Workspaces) into one Specialty References section - Remove Searching Issues section (already covered by q= in endpoints table) - Simplify Step 8 multiline bash example to a one-liner note - Remove Comment Style example code block (rules already clear from prose) - Compress Planning: replace bash block with inline endpoint reference All critical functionality preserved. Niche details remain accessible through the references/ directory. Co-Authored-By: Paperclip <noreply@paperclip.ing>
Contributor
|
Too many files changed for review. ( |
14 tasks
|
Thanks for the contribution! This PR has been inactive for a while and has drifted from the current codebase. Closing during triage to keep the queue manageable — please reopen or resubmit if it's still relevant. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reduces the per-run context overhead of the paperclip skill by removing redundant, verbose, and niche content from SKILL.md.
Size change: 26,940 → 21,667 bytes (−19.6%). vs installed production version: −34.5% (33,046 → 21,667 bytes).
What was cut
What was preserved
All critical functionality: auth env vars, scoped-wake fast path, all 9 heartbeat steps, execution-policy wake handling, blocker relationships, board approval, Critical Rules, Comment Style, Planning format, Key Endpoints table, and specialty reference pointers.
Closes ANGA-987