Skip to content

trim: cut paperclip SKILL.md by 19.6% (27KB → 21.7KB) - #4486

Closed
anhermon wants to merge 223 commits into
paperclipai:masterfrom
anhermon:feature/anga-987-trim-paperclip-skill
Closed

anhermon wants to merge 223 commits into
paperclipai:masterfrom
anhermon:feature/anga-987-trim-paperclip-skill

Conversation

@anhermon

Copy link
Copy Markdown

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

  • Duplicate Status Quick Guide (appeared twice under Step 8)
  • Manual CLI note in Authentication (not relevant to heartbeat agents)
  • Issue Dependencies JSON code blocks (redundant; converted to compact prose)
  • Board Approval full JSON example (replaced with inline field description)
  • 4 separate pointer sections merged into one Specialty References section
  • Searching Issues section (covered by q= in endpoints table)
  • Step 8 multiline bash example (scripts/paperclip-issue-update.sh)
  • Comment Style example code block
  • Planning bash block

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

anhermon and others added 30 commits April 3, 2026 05:11
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>
Cowork signal files should never be committed to the repo.
Fixes PRs #22 and #23 accidentally including these files.

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>
anhermon and others added 24 commits April 24, 2026 01:57
* 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>
@greptile-apps

greptile-apps Bot commented Apr 25, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (507 files found, 100 file limit)

@commitperclip

commitperclip Bot commented Jul 31, 2026

Copy link
Copy Markdown

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.

@commitperclip commitperclip Bot closed this Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants