Imported from knpkv/npm (
AGENTS.md). Install upstream withnpx skills add knpkv/npm. Copyright stays with the author.
Gemini Code Understanding
This document provides a comprehensive overview of the @knpkv package collection, a monorepo for npm packages. It's designed to be a quick-start guide for developers and a context file for AI assistants.
Project Overview
This is a pnpm workspace-based monorepo containing npm packages published under the @knpkv scope. The project is built with TypeScript and leverages Effect-TS for robust, type-safe functional programming.
Key Technologies
- pnpm Workspaces: Manages the monorepo structure.
- TypeScript: The primary programming language.
- Effect-TS: Used for functional programming patterns and error handling.
- Vitest: The testing framework.
- ESLint and Prettier: For code linting and formatting.
- Changesets: For versioning and changelog generation.
- Nix and direnv: For reproducible development environments.
Repository Structure
The repository is organized as follows:
npm/
├── packages/ # Published npm packages
├── .github/ # CI/CD workflows for automated checks
└── scripts/ # Build and maintenance scripts
Building and Running
The following commands are essential for working with this project.
Installation
Install all dependencies using pnpm:
pnpm install
Core Commands
-
Build all packages:
pnpm build -
Run all tests:
pnpm test -
Type-check all packages:
pnpm check -
Lint all packages:
pnpm lint -
Format all packages:
pnpm format
Development Conventions
This project adheres to a strict set of development standards to ensure code quality and consistency.
Coding Style
- Functional Programming: Code is written using functional programming principles, with a strong emphasis on the Effect-TS library.
- Type Safety: TypeScript's
strictmode is enabled, and theanytype is disallowed. - Modularity: The monorepo is divided into individual packages, each with a specific purpose.
Testing
- Comprehensive Tests: All packages are expected to have comprehensive tests written with
@effect/vitest. - Test-Driven Development: While not explicitly stated, the emphasis on testing suggests that TDD is a recommended practice.
Review Findings Become Guardrails
Treat every confirmed review finding as both a defect to fix and a prevention opportunity. Before closing the finding, classify the most durable guardrail that would catch the same defect class earlier:
- Prefer an
ast-greprule for mechanically recognizable source patterns. - Prefer an ESLint rule or configuration when scope-, binding-, control-flow-, or type-aware JavaScript/TypeScript semantics are required.
- Add a focused automated test when the invariant is behavioral or integration-level.
- Add a concise instruction to this file only when the invariant requires human or agent judgment.
Ship the applicable guardrail with the fix and prove it catches the original failure shape. If no stable automated guardrail is possible, record why in the review resolution instead of adding a brittle one-off rule.
Review agents must include a Prevention note with every finding. It should propose the concrete static-analysis matcher or lint rule when the defect is mechanically recognizable, otherwise name the behavioral test or repository instruction that should protect the invariant. A reviewer may recommend no new rule only with a short explanation of why the pattern cannot be detected reliably without excessive false positives.
Make every Prevention note implementation-ready:
- classify it as
ast-grep,ESLint,type-check,test,instruction, ornone; - name the existing rule or configuration to extend before proposing a new one;
- identify the intended rule/configuration file and the source paths it should cover;
- sketch the matcher or invariant precisely enough for the remediation agent to implement it;
- name one invalid fixture that must fail and one nearby valid fixture that must continue to pass;
- call out likely false positives, generated/vendor exclusions, and any cases that still require judgment.
Manual acceptance checklists must contain one explicit item for every manually named SC flow; a grouped
row may cover several flows only when each is named, and a checklist cannot pass while any item is
PENDING, failed, or unresolved. Capability-boundary decisions must stay synchronized across the
owning plugin/barrels, runtime documentation, package README, source requirements, and governing ADR;
an alternate authorization path must not contradict a provider-enforced prerequisite.
Keep Herdr worker-relationship behavior synchronized across
packages/herdr-fleet/README.md, packages/herdr-coordinator/README.md, and
packages/herdr-fleet/src/service.ts: consult and transition_summary accept
relationship-free coordinator roots; review and work require the exact child
relationship.
The remediation pass must implement the proposed guardrail with the defect fix whenever the proposal is stable. It must run the narrow rule fixtures first and then the complete lint/test gate. If implementation reveals that the proposal is brittle, record that evidence and replace it with the next most durable enforcement layer instead of silently dropping prevention work.
GitHub workflow guards must compare external action owner/repository names
case-insensitively, normalize action input names before inspecting them, and
reject duplicate inputs that collide after case normalization. In
pull_request_target, treat every pull-request-derived
revision, including head.sha, head.ref, github.head_ref, and
merge_commit_sha, plus head.repo.full_name checkout repositories, in dot or
static indexed syntax, as attacker-controlled when the job can access repository
credentials. Match the head.repo expression prefix so composed owner/name
repository inputs cannot bypass the guard. Treat effective workflow/job
id-token: write, any token permission with write access, and write-all as
credential authority too. On pull_request_target, omitted effective
permissions conservatively imply privileged token authority; an explicit
read-only permission map remains non-authoritative;
OIDC-bearing jobs must not checkout or build pull-request revisions. After an
attacker-controlled checkout, conservatively treat every later run, local
action, or external action step as capable of executing the workspace; a
metadata-only external action needs explicit human judgment before any narrow
allowlist exception is added.
Credential authority must follow static local reusable-workflow calls
transitively, including secrets: inherit; reject cycles and missing local
callees. Credential- or OIDC-bearing remote and dynamically constructed
reusable-workflow references must emit an explicit-review diagnostic unless a
repository-maintained reviewed allowlist proves them metadata-only. Parse ${{ ... }} delimiters without treating
}} inside quoted GitHub expression strings as the end of the expression.
Treat mechanically recognizable git checkout, git switch, and
git reset --hard commands that reference pull-request head/ref or merge
expressions in their parsed revision operand as attacker-controlled worktree
transitions. Account for value-taking global Git options such as -C and -c;
only parse git in a simple shell executable position, and keep metadata-only
logging of expressions or complete Git command text allowed.
Manual local reusable-workflow calls using secrets: inherit require the same
main-ref condition and protected environment as direct long-lived secret use.
Workflow action-pin validation must distinguish job-level reusable workflows
from step-level local actions using YAML context rather than path suffix, and
traverse every reachable repository-local action manifest, regardless of its directory, reject missing or cyclic local
action references, and apply immutable external-reference rules transitively;
Docker action runs.image references must use a digest, while a local
Dockerfile remains subject to explicit base-image review.
External-resource tests must register scope cleanup immediately after successful creation, before validating or transforming the returned resource identity.
Runtime startup tests must observe the natural supervised lifecycle path with synchronization primitives; do not add production control-flow options solely to make tests deterministic.
Lifecycle polling, admission, and drain sequencing shared by multiple workers must live in one private runtime helper.
Sandbox startup must not report readiness while legacy unauthenticated
containers may remain active; transient Docker unavailability and reconciliation
failures must retry under the supervised startup lifecycle until shutdown is
confirmed. When the database proves there are no legacy unauthenticated rows,
Docker may remain unavailable without blocking web readiness and ordinary
maintenance must retry in a supervised background loop. Query terminal as well
as active legacy rows; every legacy row must discover every container bearing
its codecommit.sandbox.id label and block readiness until all discovered and
persisted containers are stopped. Activate the owner bootstrap token's expiry and advertise or open its
URL only after the authenticated listener layer has built successfully.
Public motion-ownership props must document their default, affected surfaces and presentations, sampling or update lifetime, exit behavior, and reduced-motion interaction. Cover both intrinsic and externally owned entry with browser-backed component examples.
Security-sensitive canonical-payload documentation and code examples must name the persisted representation and every identity input. Raw provider secrets must not be described as durable payload fields, and idempotency examples must include every identity component used by production.
Security documentation in .specs/** and package READMEs must distinguish server-private provider locators from normalized or client-visible representations. Name a bucket, key, ARN, token, or similar coordinate only with its private boundary, and list the safe fields that may cross normalization or HTTP boundaries.
For packages/browser-pairing/src/**, structs containing PairingCode, SessionToken, or CsrfToken must be documented as credential-bearing; do not describe those payloads as secret-free. Credential-free summaries may retain a secret-free description.
Every provider fixture-locator list must classify each coordinate as server-private or name its safe normalized/authenticated boundary, persisted representation, and prohibited emission surfaces.
For packages/control-center/README.md, packages/control-center/src/api/**, and packages/control-center/src/client/**, an identifier that crosses an authenticated HTTP route or browser storage boundary is client-visible and must not be described as server-private. In particular, document pluginConnectionId as a normalized authenticated client-visible identifier when it appears in typed routes or cross-tab storage, including that persisted representation and its unauthenticated/public emission prohibition; keep raw provider site locators and credentials server-private. Generated and vendor documentation are excluded, while identifiers that never cross a transport boundary still require judgment.
Versioning and Publishing
- Semantic Versioning: The project uses Changesets to manage versioning and generate changelogs.
- Feature Classification: In
.changeset/*.md, exported or user-visible functionality added under publishablepackages/*/srcorpackages/*/package.jsonrequires aminorbump. This includes additive fields in exported interfaces and schemas, even when their producer or decoder is implemented privately. A new public option or application workspace is not a patch; dependency-only stabilization may remain a patch. Private, generated, and vendor packages are excluded, while internal-only features still require judgment. - Breaking Classification: An incompatible exported type or schema change requires at least a
minorbump, withmajorretained for packages whose stability contract requires it. AStream<Uint8Array>toUint8Arraychange in an exported service result paired withpatchis invalid; the same change in an unexported internal result may remain a patch. Private, generated, and vendor packages are excluded, while structurally exposed types still require judgment. - Automated Releases: The CI/CD pipeline automates the release process. When a version PR is merged, the packages are automatically published to
npm.
Generated source exposed through a publishable package's exports remains a
public contract. Before merging a spec update, compare those exports and their
types against the base. Removing a public generated model or changing
Schema.Never to Schema.Struct cannot ship as patch; description-only changes
with unchanged contracts may. The changeset checker's generated-source exclusion
does not prove compatibility. Review release classification explicitly and use
major for incompatible changes to stable packages.
Agent Management
- Sync Agent Commands:
npx @iannuttall/dotagents
Contribution Guidelines
- Create changes with proper documentation and tests.
- Add a changeset by running
pnpm changeset. - Commit your changes.
- The CI will create a version PR automatically.
- Merge the version PR to publish the changes.
Effect Source Reference
The Effect source for the workspace's pinned release (effect@4.0.0) is available under repos/effect. Treat repos/effect as vendored reference material: read it for current APIs, tests, module structure, and local idioms, but do not import from it or edit it unless the task explicitly asks to update the subtree.
Before writing Effect code, read repos/effect/LLMS.md and use rg in repos/effect/packages to verify current APIs. Import Effect modules from stable paths such as effect/http, effect/sql, and effect/process; the former effect/unstable/* paths no longer exist.
Recommended checks:
rg "Context.Service" repos/effect/packagesrg "NodeHttpServer" repos/effect/packagesrg "Clock.currentTimeMillis" repos/effect/packages
The subtree is maintained from the canonical effect-upstream remote and must be pinned to the exact npm release tag used by the workspace. Before fetching, fail closed unless effect-upstream resolves to the exact canonical HTTPS URL. Preserve subtree update merge commits: PRs that update repos/effect must use GitHub's merge-commit method because squash or rebase merging discards the provenance checked by CI. See docs/dependency-maintenance.md for the tag-pinned subtree workflow and version-alignment checks.
Use Effect Platform modules and effect/process for runtime access. Do not read process through globalThis.process or bare process.*.
Effect Static Checks
Effect-specific agent guardrails span the syntactic rules in
ast-grep/rules/effect and the scope- or binding-aware local rules in
eslint-local-rules.cjs. Run pnpm lint as the complete gate; pnpm lint:ast
covers only the ast-grep subset. See docs/effect-static-checks.md before
adding, weakening, or working around these rules.
When writing Effect code:
- Prefer
Context.Serviceclass syntax and explicitLayer.effect/Layer.succeedlayers. - Bind services before calling methods inside generators:
const service = yield* SomeService. - In
HttpApiBuilder.group, acquire stable application services in the group callback before registering handlers so the resulting layer closes its requirements. Resolve only genuinely request-scoped services, such asCurrentSession, inside the per-request handler. - Use tagged domain errors (
Data.TaggedErrororSchema.TaggedError) and keep failures in the typed error channel. - In
packages/control-center/src/server/governance/internal/execution-store, durable provider outcome decoding, canonical verification, replay-integrity checking, transition construction, transaction ownership, and fold insertion must live in one shared private fold module. Dispatch and reconciliation modules may supply source-specific outcome material, but must not duplicate the fold state machine or persistence boundary. - In
packages/control-center/src/server/persistence/repositories/delivery-graph/read.ts, keep relationship bounding and node, projection, claim, and evidence closure hydration in the privatehydrateRelationshipClosurehelper; slice branches may supply only identities, bounds, and their projection-selection policy. - Decode untrusted JSON/body data with Schema helpers before assigning it to a domain type.
- Model provider revision and reconciliation-locator parsing as Schema transformations (including template-literal parsers for structured locators); reserve manual URL/cursor extraction for opaque transport pagination.
- When advertised plugin capabilities change, update current module and service
documentation in the same change while keeping historical-descriptor comments
explicit about their older capability surface. Check the plugin's
index.ts, every ancestor barrel that exports it, public runtime JSDoc, and package README section; retained public identifiers and historical documentation still require compatibility judgment. Generated and vendor barrels are excluded. - When PR-review sandbox naming or reconciliation ownership changes in
packages/control-center/src/server/agent/internal/PrReviewSandboxSession.ts, updatepackages/control-center/README.mdandpackages/control-center/docs/agentic-pr-review.mdin the same change, and append an amendment to the governing ADR when earlier rationale changes. Current docs must describe the server-private compact workspace-scoped prefix, its 63-character sbx limit, and state that foreign-workspace and legacy names are not automatically removed; a claim that startup removes allcc-pr-review-*names is invalid. Keep the focused sandbox-session test proving that the invalid full-UUID shape exceeds the limit while the bounded compact name and foreign-workspace fixture pass. Generated and vendor docs are excluded. Clearly historical implementation plans may remain unchanged, but ADR history requires an amendment rather than a silent rewrite. - When PR-review execution placement, provider-network authority, or retained
provider user configuration changes in
packages/control-center/src/server/agent/internal/PrReviewSandboxSession.ts, updatepackages/control-center/CONTEXT.md,packages/control-center/README.md, andpackages/control-center/docs/agentic-pr-review.md, and append an amendment topackages/control-center/docs/adr/0009-use-a-provider-neutral-agent-tool-loop.md. Typed-tool review keeps its provider on the host and denies sandbox network access. Native Codex and Claude execute inside sbx with only the selected provider connection; authentication remains behind sbx-owned configuration and its credential proxy, and raw provider credentials never enter Control Center or the reviewed checkout. Output-label and error-copy changes alone do not alter this boundary. Generated and vendor docs are excluded; whether a configuration flag changes authority still requires review judgment. - Do not use raw host APIs in Effect code: no bare
process,fs,fetch,Date.now(), zero-argumentnew Date(),setTimeout, orsetInterval. UseStdio,FileSystem,HttpClient,Clock,Effect.sleep,Schedule, andeffect/processinstead. Framework/UI boundaries may use host APIs only where the framework requires them. - In
packages/*/src/client/**/*.css, use Rly service-color tokens only for provider-owned provenance (such as a CodeCommit revision rail or provider mark), never for arbitrary user-authored links or content. Use generic action/text tokens for those links. A.prRow:hover .prTitlerule using a service token is invalid because the title is user-authored; a revision-rail rule using that provider's service token remains valid. Generated and vendor styles are excluded, and ambiguous selector provenance requires judgment. - The sole raw Node filesystem exception is
packages/codecommit-core/src/CacheService/internal/PrivateDatabasePathNode.ts: it is an audited descriptor boundary that must retainO_NOFOLLOWdirectory and database handles throughfchmodand verify path identity before return. Do not broaden its ast-grep exclusion or move ordinary filesystem work into it. ChildProcess.makeoptions that setenvmust also stateextendEnv; it defaults to falsy, soenvalone replaces the child environment and dropsPATH.local-rules/require-explicit-child-process-env-inheritanceis the single enforcement layer, deliberately: deciding whether a receiver namedChildProcessis really Effect's module needs binding resolution, so a syntactic ast-grep companion reported foreign APIs of the same shape and was removed rather than narrowed. The rule enforces that the choice is stated, not that it is correct — two things still need judgment. WithextendEnv: false,envmust itself carry everything the child needs, includingPATH. WithextendEnv: true, inherited variables that outrank the ones you pass must be cleared: a spawn scoped to an explicit AWS profile has to drop every ambient environment credential provider, which means the static keys and the web-identity variables, plus bothAWS_REGIONandAWS_DEFAULT_REGION, since the AWS credential chain resolves environment variables above profile configuration. Clear each family completely — clearing one variable of a pair is worse than clearing neither, because which one leaks then depends on the caller's shell. UseChildEnv.profileScopedEnvin thecodecommitpackages rather than rebuilding the exclusion list; it documents which variables are deliberately left alone and why. It takes the environment the child will inherit as its first argument and tombstones the spellings actually present as well as the canonical names, because environment names are case-insensitive on Windows and an exact-case tombstone alone leavesAws_Access_Key_Idalive to outrank the requested profile. Obtain that environment fromChildEnv.HostEnvironment, whose layer is bound at each executable boundary — the only place permitted to read the host process. Passing an empty map is never correct at a runtime call site: it silently degrades to exact-case clearing. The folding is unconditional on every platform, which is broader than POSIX strictly needs; that is deliberate and documented in the module rather than gated on a platform read.- CodeCommit pull-request queues must hide accounts the user switched off. The
cache deliberately keeps their rows so re-enabling needs no provider round
trip, so hiding happens on read, per surface, never by pruning the cache.
In the TUI that is
AppState.pullRequests, filtered byPRServiceat every publication; in the browser it isqueuePullRequests, used by the PR queue and its filter sidebar. URL-addressable surfaces resolve against the whole cached list and stay unfiltered — the PR detail route, the Relay dock's locator lookup, the stats lists, and the cache text search — because a URL may name a pull request the queue hides; do not filter the SSE payload or its failure path, or those views dead-end. Enablement is always the persisted config, neverAppState.accounts, which is a profile-detection snapshot and is empty without a readable~/.aws/config; the browser gets it as the SSE payload'senabledProfiles, which the client's own wire schema inuseSSE.tsmust decode — it is a separate schema from the server's, and a field missing there is silently dropped. Distinguish "not known" from "none enabled": absent orundefinedlists everything rather than blanking the queue, an empty set hides everything. At publication time the config is re-read after the cache read, so long-running work does not filter with the set it opened with, and an unreadable config skips the publish and records a stale-list error rather than reporting a clean refresh. The two reads that precede their cache read are deliberate:resolveAccountsopens the refresh, where an unreadable config is fatal, and thePRServiceseed publishes nothing until the first refresh. Enforced bypackages/codecommit-core/test/PRService.visibility.test.tsandpackages/codecommit-web/test/queue-pull-requests.test.tsrather than statically:PullRequestRepo.findAllandAppState.pullRequestshave many legitimate callers, and what distinguishes a violation is whether the rows reach a queue, which no syntactic matcher can decide without flagging all of them. - When CodeCommit TUI changes add an AWS operation, Git transport behavior, or a
required local executable, update
packages/codecommit/README.mdin the same change with the corresponding IAM action and runtime prerequisite. Pure presentation changes do not require a capability update. - Keep Jira Clockify Neovim polling path claims synchronized across
.changeset/*.md,packages/jira-clockify/**, and the owning Lua fixtures. Poll coordination follows jcf's fixed~/.jcf/state.jsonauthority, not the plugin's configurable display-onlystate_path. - Keep sandbox capability boundaries synchronized across
packages/codecommit-core/README.md,packages/codecommit/README.md, and the owning policy, service, projection, and security tests. The invariant is: validate before persistence and Docker execution; require immutable image digests, migrating only the former built-incodercom/code-server:latestdefault to the current pinned digest during load; reserve code-server credential variables; accept only existing canonical children of the physical~/.codecommit/sandbox-volumesdirectory mounted below/home/coderor the exact/tmp/.local/share/code-serverruntime data subtree; keep built-in setup presets unprivileged; persist the generated access password only in an owner-only0700cache directory and0600database after rejecting symbolic-link paths, and expose it only through the authenticated, non-cacheable single-sandbox route; map the non-root container identity to the workspace owner (repair root-owned clones to a fixed non-root identity); drop all capabilities and publish only on loopback; keep sandbox browser origins on the alternate loopback hostname so the host-only owner cookie cannot reach sandbox ports; advertise the Vite origin during development while proxying bootstrap/API requests through the exact backend origin; redact credentials and workspace paths from list/event projections; and recreate legacy passwordless containers. Pass container environment, including the generated password, through a protected pipe-backed Docker env file rather than process arguments; environment names must be portable identifiers and values must be single-line so env-file parsing cannot inject variables. - Keep CodeCommit merge capability copy synchronized across
packages/codecommit/src/tui/ui/**,packages/codecommit/README.md, andpackages/codecommit-core/README.md. The provider request pins the reviewed source commit, while destination validation is preflight-only because CodeCommit exposes no destination compare-and-set. Copy must not promise that a three-way merge uses the reviewed base if the destination advances after preflight. Generated and vendor documentation are excluded; providers with a real destination compare-and-set still require capability-specific judgment. - Keep CodeCommit review-publication terminology synchronized across
.changeset/*.md,packages/codecommit/README.md, and the publication schema. CodeCommit has no native file-comment target: describe file-scoped findings as file-anchored PR comments unless the schema and provider operation actually add a distinct capability. Generated and vendor changelogs are excluded; provider terminology still requires judgment. - Keep CodeCommit editor documentation synchronized with exact-head behavior in
packages/codecommit/README.mdandpackages/codecommit/src/tui/review-session.ts: after-side findings may open at their line, before-side findings must not apply a base line to the head file, and deleted files are not launchable unless a separate verified base artifact is explicitly materialized. Also documentcodecommit:GetBlobas mandatory whenever exact-line publication validation reads provider blobs, even when local checkout powers the displayed diff.
Before enabling a production lazy authority-bearing runtime registry, a missing-record assertion is not provider coverage. The composition suite must also seed an authorized action, cross the runtime registry and executor projection, and assert the exact provider-call count and durable result.
