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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@ vendor/
CLAUDE.md
grumphp.yml
Makefile

**/node_modules/
landing/dist/
config/jwt/
.env
.env.*
47 changes: 39 additions & 8 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ jobs:

- name: Check HTTP reachability
run: |
for i in {1.10}; do
if curl -s -f http://localhost:8000/api/v1 > /dev/null 2>&1; then
for i in {1..10}; do
if curl -s -f http://localhost:8000/health > /dev/null 2>&1; then
echo "App is ready"
break
fi
echo "Waiting for app... ($i/10)"
sleep 2
done
curl -v --fail-with-body http://localhost:8000/api/v1 || true
curl -v --fail-with-body http://localhost:8000/health

- name: Create test database
run: docker compose exec -T app php bin/console -e test doctrine:database:create --if-not-exists
Expand All @@ -68,7 +68,7 @@ jobs:
run: docker compose exec -T app vendor/bin/phpunit

- name: Run PHPUnit with coverage
run: docker compose exec -T -e XDEBUG_MODE=coverage app vendor/bin/phpunit --testsuite=Unit --coverage-clover var/coverage/clover.xml --coverage-text
run: docker compose exec -T -e XDEBUG_MODE=coverage app vendor/bin/phpunit --coverage-clover var/coverage/clover.xml --coverage-text

- name: Check coverage threshold (80%)
run: |
Expand All @@ -89,9 +89,8 @@ jobs:
run: docker compose exec -T app vendor/bin/behat --no-interaction --colors

- name: Audit PHP dependencies
# Exit 1 = vulnerabilities (blocking), exit 2 = abandoned packages (non-blocking)
run: |
docker compose exec -T app composer audit || { code=$?; [ "$code" -eq 2 ] || exit "$code"; }
# Vulnerabilities and abandoned packages block CI.
run: docker compose exec -T app composer audit

- name: Run CS Fixer (dry-run)
run: docker compose exec -T app vendor/bin/php-cs-fixer fix --dry-run --diff
Expand All @@ -106,7 +105,7 @@ jobs:
run: docker compose exec -T app php bin/console lint:yaml config

- name: Doctrine Schema Validator
run: docker compose exec -T app php bin/console -e test doctrine:schema:validate --skip-sync || true
run: docker compose exec -T app php bin/console -e test doctrine:schema:validate --skip-sync

secret-scan:
name: Secret Scan
Expand Down Expand Up @@ -135,6 +134,12 @@ jobs:
- name: Build prod image
run: docker build -f docker/frankenphp/Dockerfile -t signalist:ci .

- name: Validate patched runtime
run: |
docker run --rm --entrypoint frankenphp signalist:ci build-info | grep -F 'golang.org/x/net' | grep -F 'v0.60.0'
docker run --rm --entrypoint php signalist:ci -v
docker run --rm --entrypoint frankenphp -e APP_DOMAIN=http://localhost signalist:ci validate --config /etc/caddy/Caddyfile

- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@v0.35.0
with:
Expand Down Expand Up @@ -199,6 +204,32 @@ jobs:
- name: Audit JS dependencies
run: npm audit --audit-level=high

bluesky:
name: Bluesky adapter
runs-on: ubuntu-latest
defaults:
run:
working-directory: integrations/bluesky
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: integrations/bluesky/package-lock.json

- name: Install dependencies
run: npm ci

- name: Run offline SDK tests
run: npm test

- name: Audit dependencies
run: npm audit --audit-level=high

frontend:
name: Frontend
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
/landing/.astro/
###< Landing ###

/integrations/bluesky/node_modules/

###> friendsofphp/php-cs-fixer ###
/.php-cs-fixer.php
/.php-cs-fixer.cache
Expand Down
37 changes: 28 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,35 @@ Execution principles:
- feature work: add the right test level instead of only broad smoke coverage
- refactor: verify behavior before and after with the relevant checks

Ask first:

- adding composer or npm packages
- changing PostgreSQL schema or pgvector dimensions
- changing AI provider configuration or prompt contracts
- changing the project MCP protocol implementation
- changing `phpstan.neon`
- changing shared instruction files
## Autonomous Execution

An explicit request to implement a feature, fix, or roadmap authorizes the necessary local implementation and verification. Continue through the requested scope without asking for permission for each routine step. State material assumptions, use existing conventions, and report progress and blockers.

Within the requested scope, work autonomously on:

- adding necessary composer or npm packages after checking compatibility
- changing PostgreSQL schema and pgvector dimensions through reviewed, tested migration files; apply migrations only to local development or test databases
- implementing AI provider configuration and versioned prompt contracts without exposing secrets or sending raw personal data
- implementing application MCP endpoints with authentication, authorization, and scoped output; this does not authorize external agent runtime MCP access
- starting local development services and running relevant quality gates
- making reversible local code, configuration, test, and documentation changes required to complete the task

Use configurable defaults when external accounts or product decisions are unavailable. Complete and test independent work while reporting the remaining setup. Ask for clarification only when a missing decision materially affects correctness, security, cost, or irreversible behavior.

Keep fixing PHPStan findings in code, types, or PHPDoc. Do not weaken analysis or quality gates to obtain passing checks. Changes to `phpstan.neon` still require explicit approval.

Ask first for:

- destructive operations that discard existing user work or data
- production database changes, public deployment, or publishing
- live external messages, email delivery, social posts, charges, or paid API usage unless already explicitly authorized
- enabling external agent runtime MCP access
- shared instruction changes that were not explicitly requested or approved in the current conversation
- running `git commit`
- running `git push`

Explicit authorization persists for the approved scope in the current conversation; do not ask again for the same action. These autonomous execution rules take precedence over stricter routine-step approval guidance in repo-local workflows. Preserve security and privacy requirements.

Never:

- commit directly to `main`, `master`, or `develop`
Expand Down Expand Up @@ -180,7 +198,8 @@ Policy:
- duplicated or conflicting guidance
- only reusable, stable guidance should be added
- temporary context, one-off fixes, and local anecdotes should not be added to instruction files
- changes to instruction files must be proposed first and applied only after explicit confirmation in the current conversation
- agent-initiated instruction improvements must be proposed first and applied only after explicit confirmation in the current conversation
- an explicit user request to change instruction files authorizes that requested edit; explain the intended change and apply it without a second confirmation

## Quality Gates

Expand Down
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.PHONY: help up down build rebuild shell logs lint analyse tests-unit tests-integration tests db-migrate db-reset cache-clear composer-install composer-update front-install front-dev front-build front-preview front-lint front-test front-test-watch front-hooks grumphp

RECTOR_FLAGS ?=

# Default target
.DEFAULT_GOAL := help

Expand Down Expand Up @@ -75,7 +77,7 @@ analyse: ## Run PHPStan static analysis
docker compose exec app vendor/bin/phpstan analyse

rector: ## Run Rector to refactor code
docker compose exec app vendor/bin/rector process
docker compose exec app vendor/bin/rector process $(RECTOR_FLAGS)

rector-dry: ## Run Rector in dry-run mode
docker compose exec app vendor/bin/rector process --dry-run
Expand All @@ -96,8 +98,8 @@ tests-integration: ## Run integration tests
tests: ## Run all tests
docker compose exec app vendor/bin/phpunit

tests-coverage: ## Run unit tests with coverage report (HTML + text)
docker compose exec -e XDEBUG_MODE=coverage app vendor/bin/phpunit --testsuite=Unit --coverage-html var/coverage --coverage-text
tests-coverage: ## Run backend behavior tests with coverage report (HTML + text)
docker compose exec -e XDEBUG_MODE=coverage app vendor/bin/phpunit --coverage-html var/coverage --coverage-text

tests-api: ## Run Behat API tests
docker compose exec app vendor/bin/behat --colors
Expand Down
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,29 @@ publishing insights across newsletters and social media.
**Monitor → Curate → Synthesize → Publish**

- **Aggregate** RSS feeds into categorized, searchable inboxes
- **Summarize** articles automatically with LLMs (OpenAI, Anthropic, Mistral)
- **Bookmark & tag** content with AI-assisted auto-tagging
- **Enrich** articles through a provider-independent AI port (live provider setup deferred)
- **Bookmark & tag** content, with owner-scoped tag management and optional AI tagging
- **Generate newsletters** from curated articles, configurable by reading time
- **Publish** to social media (X, LinkedIn, Threads, Bluesky, WhatsApp)
- **Search** with full-text and semantic (vector) search
- **Share** links to X, LinkedIn, Threads, Bluesky and WhatsApp; approve API publishing to configured LinkedIn, X and Bluesky accounts
- **Search** with full text; semantic/hybrid modes become available with a configured embedding provider
- **Collaborate** in workspaces with roles, annotations and independent read status
- **Control privacy** with consent, export, restriction and password-confirmed deletion
- **Manage plans** through configured Stripe checkout, portal and signed webhooks

---

Implementation and release configuration: [deployment guide](docs/DEPLOYMENT.md). External provider integrations are disabled until credentials and account approvals are supplied. Production deployment and live provider validation remain outstanding.

## Tech Stack

| Layer | Technology |
|-------|------------|
| **Backend** | PHP 8.5 · Symfony 7.4 · API Platform 4.x · FrankenPHP |
| **Frontend** | React 19 · TypeScript · Vite 7 · MUI v7 · react-i18next |
| **Database** | PostgreSQL 16 · pgvector |
| **Landing** | Astro 7 · Tailwind v4 · Node 22.19+ |
| **Queue** | Symfony Messenger · Redis |
| **AI** | Symfony AI (OpenAI / Anthropic / Mistral) |
| **AI** | Provider-independent adapters; live provider setup deferred |
| **Auth** | LexikJWT |
| **Testing** | PHPUnit · Behat · Vitest · React Testing Library |

Expand Down
15 changes: 12 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,35 @@
"ext-ctype": "*",
"ext-iconv": "*",
"ext-pdo_pgsql": "*",
"ext-sodium": "*",
"api-platform/doctrine-orm": "^4.2",
"api-platform/symfony": "^4.2",
"doctrine/dbal": "^3.10",
"doctrine/doctrine-bundle": "^2.18",
"doctrine/doctrine-migrations-bundle": "^3.7",
"doctrine/orm": "^3.5",
"laminas/laminas-feed": "^2.26",
"laminas/laminas-http": "^2.23",
"lexik/jwt-authentication-bundle": "^3.2",
"mcp/sdk": "^0.8.1",
"nelmio/cors-bundle": "^2.6",
"nyholm/psr7": "^1.8",
"runtime/frankenphp-symfony": "^0.2",
"stripe/stripe-php": "^22.0",
"symfony/console": "7.4.*",
"symfony/dotenv": "7.4.*",
"symfony/flex": "^2.10",
"symfony/framework-bundle": "7.4.*",
"symfony/http-client": "7.4.*",
"symfony/mailer": "7.4.*",
"symfony/messenger": "7.4.*",
"symfony/process": "7.4.*",
"symfony/property-access": "7.4.*",
"symfony/property-info": "7.4.*",
"symfony/psr-http-message-bridge": "^7.4",
"symfony/rate-limiter": "7.4.*",
"symfony/redis-messenger": "7.4.*",
"symfony/runtime": "7.4.*",
"symfony/scheduler": "7.4.*",
"symfony/security-bundle": "7.4.*",
"symfony/serializer": "7.4.*",
"symfony/twig-bundle": "7.4.*",
Expand All @@ -40,10 +48,11 @@
},
"require-dev": {
"behat/behat": "^3.29",
"behatch/contexts": "^1.3",
"behat/mink": "^1.13",
"behat/mink-browserkit-driver": "^2.3",
"doctrine/doctrine-fixtures-bundle": "^4.3",
"enlightn/security-checker": "^2.0",
"friends-of-behat/mink-browserkit-driver": "^1.6",
"friends-of-behat/mink-extension": "^2.7",
"friends-of-behat/symfony-extension": "^2.6",
"friendsofphp/php-cs-fixer": "^3.91",
"phpro/grumphp": ">=2.18",
Expand Down
Loading
Loading