Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
cooldown:
default-days: 1
include:
- "*"
exclude:
- "@conduction/*"
6 changes: 6 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
legacy-peer-deps=true

# Supply-chain hardening: reject any npm package published less than
# 24h ago. Compromised first-party-Conduction packages are excluded via
# Dependabot cooldown (.github/dependabot.yml); for fresh @conduction/*
# releases, override per-install with `npm install --min-release-age=0`.
min-release-age=1
1 change: 1 addition & 0 deletions docs/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const config = createConfig({
/* themeConfig is shallow-merged into the preset's defaults
(colorMode + navbar + footer). prism + mermaid land alongside. */
themeConfig: {
image: 'img/og-pipelinq.png',
prism: {
theme: require('prism-react-renderer/themes/github'),
darkTheme: require('prism-react-renderer/themes/dracula'),
Expand Down
11 changes: 7 additions & 4 deletions docs/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"postbuild": "validate-ai-baseline",
"validate:ai-baseline": "validate-ai-baseline",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
Expand All @@ -15,7 +17,7 @@
"ci": "npm ci --legacy-peer-deps && npm run build"
},
"dependencies": {
"@conduction/docusaurus-preset": "^2.6.1",
"@conduction/docusaurus-preset": "^3.6.0",
"redocusaurus": "^2.0.0",
"@docusaurus/core": "^3.7.0",
"@docusaurus/preset-classic": "^3.7.0",
Expand Down
Binary file added docs/static/img/og-pipelinq.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions docs/static/llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# PipelinQ

> Pipelinq is an open-source CRM and pipeline-management app for the Nextcloud workspace.

Pipelinq is an open-source CRM and pipeline-management app for the Nextcloud workspace. It runs a kanban deal-flow with configurable stages, per-team filters via Nextcloud groups, and KPI cards on top. Customers, contacts, and deals are typed OpenRegister schemas with citation-stable IDs and a per-record audit trail. Quotes are generated through DocuDesk templates, and OpenConnector links to accounting systems such as Exact, Twinfield, and AFAS. Released under EUPL-1.2 and maintained by Conduction since 2019.

## Docs

- [Documentation](https://pipelinq.conduction.nl/docs/intro): main entry, including tutorials, user guide, and admin guide.
- [API reference](https://pipelinq.conduction.nl/api): OpenAPI documentation.

## Optional

- [Install](https://www.conduction.nl/install): self-host on your Nextcloud instance.
- [Source code](https://github.com/ConductionNL/pipelinq): repository and issue tracker.
- [App page](https://www.conduction.nl/apps/pipelinq): product positioning on conduction.nl.

## Contact

- Email: info@conduction.nl
- Web: https://www.conduction.nl
- GitHub: https://github.com/ConductionNL
- Conduction B.V. · KvK 76741850 · Lauriergracht 14h, Amsterdam, Netherlands
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Split per ADR-032

This change was archived on 2026-05-18 because its tasks.md held 47 unchecked
items — over Hydra ADR-032's spec-sizing cap (≤20 unchecked tasks per change).

The original `proposal.md`, `design.md`, and `specs/pipelinq-or-adoption/spec.md`
remain in this archive folder for reference; sub-changes reference back here for
the full design narrative.

## Split into

- `pipelinq-or-register-resolver` (9 tasks) — Phase 1 register-resolver
migration (8 sites + verify).
- `pipelinq-or-lifecycle-notification` (7 tasks) — Phase 2 lifecycle annotation
+ Phase 3 notification annotation.
- `pipelinq-or-archival-calculations` (6 tasks) — Phase 4 archival annotation
+ Phase 5 calculation annotation.
- `pipelinq-or-spec-rewrites` (6 tasks) — Phase 6 spec rewrites + Phase 10
createObjectStore exemplar declaration.
- `pipelinq-admin-config-magic-numbers` (12 tasks) — Phase 7 hardcoded
magic-number cleanup (12 constants → admin-config).
- `pipelinq-manifest-i18n-tenant` (7 tasks) — Phase 8 manifest adoption +
Phase 9 multi-tenancy + i18n adoption.

Total: 47 tasks across 6 sub-changes. Each sub-change is ≤20 unchecked tasks.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Split per ADR-032

This change was archived on 2026-05-18 because its tasks.md held 30 unchecked
items — over Hydra ADR-032's spec-sizing cap (≤20 unchecked tasks per change).

The original `proposal.md` and `tasks.md` remain in this archive folder for
reference; sub-changes reference back here for the parent proposal.

## Split into

- `pipelinq-quality-phpcs-burndown` (9 tasks) — Phase 1 inventory/planning
+ Phase 2 PHPCS exclude-pattern burn-down.
- `pipelinq-quality-phpmd-burndown` (9 tasks) — Phase 3 PHPMD burn-down +
PHPMD-specific Phase 5 baseline cleanup.
- `pipelinq-quality-phpstan-burndown` (11 tasks) — Phase 4 PHPStan burn-down +
Phase 5 CI integration + Phase 6 documentation.

Total: tasks regrouped across 3 sub-changes. Each sub-change is ≤20 unchecked
tasks. Mirrors the `openconnector-legacy-quality-cleanup` pattern used in the
companion split.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# pipelinq: move hardcoded constants to admin-config

## Why

Split from the parent `pipelinq-adopt-or-abstractions` change per ADR-032
(spec-sizing cap: ≤20 unchecked tasks per change). See
`openspec/changes/archive/2026-05-18-pipelinq-adopt-or-abstractions-split/`
for the original bundled proposal and design.

This slice covers Phase 7 only: 12 hardcoded constants (timing intervals,
business hours, third-party API base URLs, cache TTL, default review intervals)
that must become admin-config for multi-tenant deployments. Audit citation:
`.claude/audit-2026-05-03/04-hardcoded.md`.

Defaults preserve current behavior (zero-behavior-change install).

## What Changes

1. Eight background-job timing constants → admin-config keys
(`pipelinq.kennisbank.review_interval_days`,
`pipelinq.queue_overflow.poll_interval_seconds`, three for `task_expiry`,
`pipelinq.task_escalation.threshold_hours`).
2. Two `TaskService` business-hours constants → admin-config in the tenant's
configured timezone (Europe/Amsterdam default).
3. One prospect-discovery cache TTL constant → admin-config.
4. Two third-party API base URLs (`KvkApiClient`, `OpenCorporatesApiClient`) →
admin-config so EU/UK tenants can point at regional endpoints.
5. Confirm Dutch state literals are gone (cross-check with the
lifecycle+notification slice).

## Affected Projects

- pipelinq (consumer)

## Impact

- Affected code (apply-phase hints, NOT changed here):
`lib/BackgroundJob/KennisbankReviewJob.php`,
`lib/BackgroundJob/QueueOverflowJob.php`,
`lib/BackgroundJob/TaskExpiryJob.php`,
`lib/BackgroundJob/TaskEscalationJob.php`,
`lib/Service/TaskService.php`,
`lib/Service/ProspectDiscoveryService.php`,
`lib/Service/KvkApiClient.php`,
`lib/Service/OpenCorporatesApiClient.php`.
- Affected specs: new `pipelinq-or-adoption` capability slice (delta only).
- Breaking changes: none — defaults preserve current behavior.

## See Also

- `openspec/changes/archive/2026-05-18-pipelinq-adopt-or-abstractions-split/design.md`
(Decision 5: third-party API URLs become admin-config; Decision 6: defaults
preserve behavior; Decision 8: tenant-tunable timing).
- `.claude/audit-2026-05-03/04-hardcoded.md` — audit source.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Capability — pipelinq-or-adoption (admin-config magic-numbers slice)

## ADDED Requirements

### Requirement: Tenant-tunable values move to admin-config

Hardcoded constants flagged in `.claude/audit-2026-05-03/04-hardcoded.md` SHALL move to
admin-config. Default values SHALL preserve current behavior.

#### Scenario: Background-job intervals are admin-config

- **GIVEN** an admin sets `pipelinq.task_expiry.poll_interval_seconds = 1800`
- **WHEN** `TaskExpiryJob` runs
- **THEN** the poll interval SHALL be 1800
- **AND** no `INTERVAL = 900` constant SHALL exist in `lib/BackgroundJob/TaskExpiryJob.php`.

#### Scenario: Business hours are tenant-tunable

- **GIVEN** an admin sets
`pipelinq.task.business_hour_start = 9` and `pipelinq.task.business_hour_end = 18`
- **WHEN** `TaskService` evaluates whether a moment is within business hours
- **THEN** business hours SHALL be 09:00-18:00 in the tenant's configured timezone
- **AND** no `BUSINESS_HOUR_START = 8` or `BUSINESS_HOUR_END = 17` constant SHALL exist
in `lib/Service/TaskService.php`.

#### Scenario: Third-party API base URLs are admin-config

- **GIVEN** an admin sets `pipelinq.kvk.api_base_url` to a regional endpoint
- **WHEN** `KvkApiClient` makes a request
- **THEN** the request SHALL go to the configured URL
- **AND** the constant `API_BASE` SHALL no longer exist in
`lib/Service/KvkApiClient.php`.

#### Scenario: Defaults preserve current behavior

- **GIVEN** a fresh pipelinq install with no admin-config overrides
- **WHEN** any service reads a value migrated under Phase 7
- **THEN** the value SHALL equal the constant value listed in
`.claude/audit-2026-05-03/04-hardcoded.md`.
44 changes: 44 additions & 0 deletions openspec/changes/pipelinq-admin-config-magic-numbers/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Tasks — pipelinq: move hardcoded constants to admin-config

ADR-032 cap respected (≤20 unchecked tasks).

Spec-only change. Code paths listed are implementation hints for the apply phase.

## Phase 7 — hardcoded magic-number cleanup

All paths per `.claude/audit-2026-05-03/04-hardcoded.md`. Each becomes admin-config (default
preserved).

- [ ] 7.1 `lib/BackgroundJob/KennisbankReviewJob.php:41` —
`DEFAULT_REVIEW_INTERVAL = 180` (days?) → admin-config
`pipelinq.kennisbank.review_interval_days` (default `180`).
- [ ] 7.2 `lib/BackgroundJob/QueueOverflowJob.php:41` — `INTERVAL = 300` (seconds) →
admin-config `pipelinq.queue_overflow.poll_interval_seconds` (default `300`).
- [ ] 7.3 `lib/BackgroundJob/TaskExpiryJob.php:43` — `INTERVAL = 900` → admin-config
`pipelinq.task_expiry.poll_interval_seconds` (default `900`).
- [ ] 7.4 `lib/BackgroundJob/TaskExpiryJob.php:50` —
`ESCALATION_THRESHOLD = 14400` → admin-config
`pipelinq.task_expiry.escalation_threshold_seconds` (default `14400`).
- [ ] 7.5 `lib/BackgroundJob/TaskExpiryJob.php:57` —
`IN_PROGRESS_GRACE = 86400` → admin-config
`pipelinq.task_expiry.in_progress_grace_seconds` (default `86400`).
- [ ] 7.6 `lib/BackgroundJob/TaskEscalationJob.php:43` —
`ESCALATION_THRESHOLD_HOURS = 4` → admin-config
`pipelinq.task_escalation.threshold_hours` (default `4`).
- [ ] 7.7 `lib/Service/TaskService.php:73` — `BUSINESS_HOUR_START = 8` → admin-config
`pipelinq.task.business_hour_start` (default `8`). NL-specific assumption removed.
- [ ] 7.8 `lib/Service/TaskService.php:80` — `BUSINESS_HOUR_END = 17` → admin-config
`pipelinq.task.business_hour_end` (default `17`).
- [ ] 7.9 `lib/Service/ProspectDiscoveryService.php:36` — `CACHE_TTL = 3600` →
admin-config `pipelinq.prospect_discovery.cache_ttl_seconds` (default `3600`).
- [ ] 7.10 `lib/Service/KvkApiClient.php:37` —
`API_BASE = 'https://api.kvk.nl/api/v1'` → admin-config
`pipelinq.kvk.api_base_url` (default `https://api.kvk.nl/api/v1`). Class is
LEGITIMATE third-party client; only the URL becomes admin-config so EU/UK regional
endpoints can be configured.
- [ ] 7.11 `lib/Service/OpenCorporatesApiClient.php:37` —
`API_BASE = 'https://api.opencorporates.com/v0.4'` → admin-config
`pipelinq.opencorporates.api_base_url`.
- [ ] 7.12 Confirm Dutch state literals from the lifecycle+notification slice are removed
from source after lifecycle migration (no `'gepubliceerd'|'nieuw'|'openbaar'`
literals in `lib/`).
56 changes: 56 additions & 0 deletions openspec/changes/pipelinq-manifest-i18n-tenant/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# pipelinq: manifest + multi-tenancy + i18n adoption

## Why

Split from the parent `pipelinq-adopt-or-abstractions` change per ADR-032
(spec-sizing cap: ≤20 unchecked tasks per change). See
`openspec/changes/archive/2026-05-18-pipelinq-adopt-or-abstractions-split/`
for the original bundled proposal and design.

This slice covers Phase 8 (manifest adoption per Hydra
`adopt-app-manifest`) and Phase 9 (multi-tenancy + i18n adoption gated on
nc-vue and OR shipping the prerequisites). Both phases declare consumed
shared specs and pair naturally as the manifest is the declarative home for
those `consumes` entries.

## What Changes

### Manifest adoption (Phase 8)

1. Create `openspec/manifest.yaml` with `tier: 3` (frontend exemplar),
`dependencies: ["openregister"]`, the consumed shared specs.
2. Pin minimum OR version (must include `register-resolver-service` and
`contacts-actions` integration provider).
3. Declare `pipelinq.role: object-store-exemplar` so other apps can find the
reference implementation.
4. Validate the manifest with the Hydra manifest schema once it ships.

### Multi-tenancy + i18n adoption (Phase 9)

5. Adopt `multi-tenancy-context` formally — pass tenant context explicitly
into `createObjectStore`.
6. Adopt `i18n-source-of-truth` for translatable fields on kennisbank, lead,
task, callback schemas.
7. Adopt `i18n-api-language-negotiation` for the pipelinq API.

## Affected Projects

- pipelinq (consumer)
- nextcloud-vue (must ship `multi-tenancy-context`)
- openregister (must ship `i18n-source-of-truth` +
`i18n-api-language-negotiation`)
- hydra (must ship `adopt-app-manifest`)

## Impact

- Affected files: new `openspec/manifest.yaml`,
`src/store/modules/object.js` (explicit tenant context),
pipelinq schemas (`label`, `description`, lifecycle-state names).
- Affected specs: new `pipelinq-or-adoption` capability slice (delta only).
- Breaking changes: none — additive.

## See Also

- `openspec/changes/archive/2026-05-18-pipelinq-adopt-or-abstractions-split/design.md`
(Decision 7: createObjectStore exemplar role flows into the manifest).
- `hydra/openspec/changes/adopt-app-manifest/` — manifest schema.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Capability — pipelinq-or-adoption (manifest + i18n + tenancy slice)

## ADDED Requirements

### Requirement: pipelinq declares its manifest

pipelinq SHALL ship `openspec/manifest.yaml` declaring `tier: 3` (frontend exemplar),
`dependencies: ["openregister"]`, the consumed shared specs, the minimum OR version, and
its exemplar role.

#### Scenario: Manifest declares exemplar role

- **GIVEN** `openspec/manifest.yaml` declares
`pipelinq.role: object-store-exemplar` (or equivalent key from `adopt-app-manifest`)
- **WHEN** Hydra coordination loads the manifest
- **THEN** it SHALL recognize pipelinq as the reference implementation.

#### Scenario: Manifest pins minimum OR version including contacts-actions

- **GIVEN** the spec-rewrites slice depends on the `contacts-actions` provider in OR
- **WHEN** the manifest declares minimum OR version
- **THEN** the version pin SHALL include the OR release that ships
`contacts-actions`.

### Requirement: pipelinq consumes shared multi-tenancy + i18n specs

pipelinq SHALL consume `multi-tenancy-context`, `i18n-source-of-truth`, and
`i18n-api-language-negotiation`.

#### Scenario: createObjectStore receives tenant context explicitly

- **GIVEN** the nc-vue `multi-tenancy-context` composable is available
- **WHEN** `src/store/modules/object.js` invokes `createObjectStore('object', {...})`
- **THEN** the factory call SHALL pass the tenant context from `useTenantContext()`
explicitly (formalising the implicit dependency).

#### Scenario: API respects Accept-Language

- **GIVEN** a client sends `Accept-Language: nl-NL` to pipelinq
- **WHEN** the response includes a translatable label or description
- **THEN** the field SHALL return the Dutch translation per OR's negotiation spec.
Loading
Loading