{"id":49528966,"url":"https://github.com/rubiss-projects/agile-tools","last_synced_at":"2026-05-29T00:00:52.590Z","repository":{"id":352553344,"uuid":"1215569998","full_name":"Rubiss-Projects/agile-tools","owner":"Rubiss-Projects","description":"Self-hosted Jira Data Center kanban flow forecasting platform with local analytics, aging analysis, and Monte Carlo delivery forecasts.","archived":false,"fork":false,"pushed_at":"2026-05-27T19:19:39.000Z","size":1545,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-27T21:09:34.143Z","etag":null,"topics":["docker","flow-analytics","flow-metrics","forecasting","github-actions","jira-data-center","kanban","monorepo","monte-carlo","nextjs","postgresql","prisma","react","self-hosted","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rubiss-Projects.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-20T03:36:18.000Z","updated_at":"2026-05-25T01:24:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"c5da8b4b-e834-4019-a4d8-0742abb3c4ac","html_url":"https://github.com/Rubiss-Projects/agile-tools","commit_stats":null,"previous_names":["rubiss/agile-tools","rubiss-projects/agile-tools"],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/Rubiss-Projects/agile-tools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rubiss-Projects%2Fagile-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rubiss-Projects%2Fagile-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rubiss-Projects%2Fagile-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rubiss-Projects%2Fagile-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rubiss-Projects","download_url":"https://codeload.github.com/Rubiss-Projects/agile-tools/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rubiss-Projects%2Fagile-tools/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33630999,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","flow-analytics","flow-metrics","forecasting","github-actions","jira-data-center","kanban","monorepo","monte-carlo","nextjs","postgresql","prisma","react","self-hosted","typescript"],"created_at":"2026-05-02T05:15:54.285Z","updated_at":"2026-05-29T00:00:52.584Z","avatar_url":"https://github.com/Rubiss-Projects.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Agile Tools — Kanban Flow Forecasting\n\nA self-hosted web application for kanban teams. Connects to a self-hosted Jira Data Center instance via a service-account PAT, synchronizes issue data on a local PostgreSQL database, and serves flow visibility, aging analysis, and story-count Monte Carlo forecasts entirely from local read models.\n\n## Features\n\n- **Jira sync** — scheduled and manual synchronization of board issues, lifecycle events, and hold periods, with graceful worker shutdown canceling in-flight syncs and stale queued/running syncs auto-failed after 60 minutes so scopes can recover without manual database cleanup\n- **Admin configuration** — create and edit Jira connections plus create, edit, and delete board/flow scope mappings from the admin UI\n- **Current flow view** — scatter plot of active work items with percentile-based aging zones and on-hold classification\n- **Work-item detail** — per-item lifecycle timeline and hold period breakdown\n- **Monte Carlo forecasting** — story-count \"when will we finish?\" and \"how many by a date?\" simulations backed by local throughput history\n\n## Prerequisites\n\n| Requirement | Version |\n|---|---|\n| Node.js | 24 LTS |\n| pnpm | 10+ |\n| Docker | any recent version |\n| Jira Data Center | 8.14+ |\n\nA Jira service-account PAT with read access to the target board, its issues, and changelog history is required. The PAT is entered through the admin UI and stored encrypted — it is never written to environment files.\n\n## Workspace Layout\n\n```\napps/web        # Next.js UI and HTTP API (port 3000 by default)\napps/worker     # Scheduled sync, manual refresh jobs, projection rebuilds\npackages/db     # Prisma schema, migrations, and database helpers\npackages/analytics\npackages/jira-client\npackages/shared\n```\n\n## Bootstrap\n\n### 1. Install dependencies\n\n```bash\npnpm install\n```\n\n### 2. Create local environment files\n\nCopy `.env.example` to `.env` and fill in the values:\n\n```bash\ncp .env.example .env\n```\n\nRequired variables:\n\n```bash\nDATABASE_URL=postgresql://postgres:postgres@localhost:5432/agile_tools\nENCRYPTION_KEY=\u003c32+ character random key — never reuse across environments\u003e\nSESSION_SECRET=\u003c32+ character random key\u003e\nDEFAULT_SYNC_INTERVAL_MINUTES=10\nLOG_LEVEL=debug\n```\n\nRecommended when you run the production web container directly on `http://localhost` without TLS termination:\n\n```bash\nALLOW_LOOPBACK_HTTP_BYPASS=true\nALLOW_LOCAL_BOOTSTRAP=true\n```\n\n### 3. Start PostgreSQL\n\n```bash\ndocker compose up -d postgres\n```\n\n### 4. Apply the database schema\n\n```bash\npnpm --filter @agile-tools/db prisma:migrate\n```\n\n### 5. Start the web application\n\n```bash\npnpm --filter @agile-tools/web dev\n```\n\n### 6. Start the background worker\n\n```bash\npnpm --filter @agile-tools/worker dev\n```\n\nOpen `http://localhost:3000` and navigate to **Admin → Jira Connections** to configure the first connection.\n\n## Operations\n\nThe web runtime exposes public OpenTelemetry metrics in Prometheus text format at `http://localhost:3000/metrics`.\nThe worker runtime exposes the same scrape format at `http://localhost:9464/metrics` when run from Docker Compose.\nScrape either endpoint from Prometheus, Grafana Alloy, or another compatible collector. If the app is reachable outside a trusted\nnetwork, restrict `/metrics` at the reverse proxy or network layer because metric labels can reveal runtime details.\n\nWorker metrics bind to `METRICS_HOST` and `METRICS_PORT`. `METRICS_PORT` takes precedence; if it is unset, the worker\nuses `PORT` so Kubernetes-style environments can assign the scrape port with the standard platform variable, then falls\nback to `9464`. The Docker Compose files publish the worker scrape port from `WORKER_METRICS_PORT`, defaulting to `9464`.\n\nMetrics include runtime scrape/process data, standard OpenTelemetry `http.server.request.duration` histograms for web\nrequests, standard OpenTelemetry `http.client.request.duration` histograms for outbound Jira REST calls, forecast and flow\nanalytics reads, manual sync enqueues, worker job and sync runs, queue depth snapshots, and Prisma query durations. In\nPrometheus text, the HTTP series are emitted as `http_server_request_duration` and `http_client_request_duration` with unit\n`s`, plus count, sum, and second-based bucket series; derive request and error rates from each metric's `_count` series\nfiltered by `http_response_status_code` or `error_type`. Jira client metrics also include low-cardinality\n`agile_tools_jira_operation` and `agile_tools_jira_result` labels. Labels intentionally avoid workspace IDs, issue keys,\nuser data, raw URLs, SQL, or JQL.\n\n## Docker Runtime\n\nThe repository now ships a single multi-stage Docker image that contains both runtime roles:\n\n- `web` for the Next.js UI and HTTP API\n- `worker` for scheduled sync and projection jobs\n\nThis is the recommended compromise for this monorepo: build one image artifact, then run separate containers for the web and worker roles. That stays aligned with Docker's one-service-per-container guidance while still keeping artifact management simple.\n\n### Build the image\n\n```bash\ndocker build -t agile-tools:local .\n```\n\n### Use the published GHCR image with consumer Compose\n\nIf you are consuming a release image instead of building from source, use [docker-compose.consumer.yml](docker-compose.consumer.yml). It pulls `${AGILE_TOOLS_IMAGE:-ghcr.io/rubiss-projects/agile-tools:latest}` directly from GitHub Container Registry with no local retag step.\n\n```bash\ndocker compose -f docker-compose.consumer.yml up -d postgres\ndocker compose -f docker-compose.consumer.yml --profile bootstrap run --rm bootstrap\ndocker compose -f docker-compose.consumer.yml --profile runtime up -d\n```\n\nIf you want to pin a specific release instead of following `latest`, set `AGILE_TOOLS_IMAGE` in your shell or `.env`, for example `ghcr.io/rubiss-projects/agile-tools:v0.1.1`.\n\nThe published image runs in production mode, so the dev demo bootstrap remains disabled there. For local image hosting, set `ALLOW_LOCAL_BOOTSTRAP=true` and use the built-in local admin bootstrap action from `/` or `/admin/jira`. The bootstrap flow is intended for loopback hosts such as `localhost` and `127.0.0.1`.\n\n### Run the web role from a local build\n\n```bash\ndocker run --rm -p 3000:3000 --env-file .env \\\n\t-e ALLOW_LOOPBACK_HTTP_BYPASS=true \\\n\t-e ALLOW_LOCAL_BOOTSTRAP=true \\\n\t-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/agile_tools \\\n\tagile-tools:local web\n```\n\n### Run the worker role from a local build\n\n```bash\ndocker run --rm --env-file .env \\\n\t-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/agile_tools \\\n\tagile-tools:local worker\n```\n\n### Bootstrap database and queue state from a local build\n\n```bash\ndocker run --rm --env-file .env \\\n\t-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/agile_tools \\\n\tagile-tools:local bootstrap\n```\n\n### Run the full runtime stack with source-build Compose\n\nThe root compose file keeps `postgres` as the default local dependency and exposes a one-off bootstrap job plus the application runtime containers behind profiles:\n\n```bash\ndocker compose up -d postgres\ndocker compose --profile bootstrap run --rm bootstrap\ndocker compose --profile runtime up --build -d\n```\n\nThe `bootstrap` role runs Prisma migrations and pg-boss schema migrations from the same image artifact. The image also supports an `all` command for running both processes in one container, but that is a convenience mode rather than the recommended production shape.\n\n### Managed-service deployments: configurable database URL source\n\nSome managed platforms inject the PostgreSQL connection string under an autogenerated variable name (for example `XYZ_POSTGRESQL_URI`) that the platform controls and the application cannot rename. To support that, the web and worker runtimes read the database URL from a configurable source:\n\n- If `DATABASE_URL_ENV_VAR` is set, the value of the variable it names is used as the database URL (for example `DATABASE_URL_ENV_VAR=XYZ_POSTGRESQL_URI` reads `process.env.XYZ_POSTGRESQL_URI`).\n- Otherwise the runtime falls back to `DATABASE_URL`, which is the default for local, dev, and docker-compose setups.\n\nIf `DATABASE_URL_ENV_VAR` is set but the referenced variable is missing or empty, the application fails at startup with a clear configuration error. The same resolution is applied by the container entrypoint, so the `bootstrap` role (Prisma migrate and pg-boss migrate) honors `DATABASE_URL_ENV_VAR` as well.\n\nThe local compose runtime defaults `ALLOW_LOOPBACK_HTTP_BYPASS=true` for the web container so `http://localhost:3000` works without an extra reverse proxy.\nIt also defaults `ALLOW_LOCAL_BOOTSTRAP=true`, which enables a loopback-only local admin bootstrap action on the unauthenticated home and admin pages.\nIf your environment depends on a custom npm registry, proxy, or relaxed TLS setting, the source-build compose path now forwards optional build-time variables such as `NPM_CONFIG_REGISTRY`, `COREPACK_NPM_REGISTRY`, `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, and `NODE_TLS_REJECT_UNAUTHORIZED` from the invoking shell. The build uses the repository's pinned `packageManager` field through Corepack. By default, Corepack uses `NPM_CONFIG_REGISTRY`, but you can set `COREPACK_NPM_REGISTRY` separately when package-manager downloads must come from a different Artifactory remote than normal package installs.\n\nThe consumer compose file uses the same service layout and defaults, but it pulls the published image from GHCR instead of building from the local checkout.\n\nOpen `http://localhost:3000` and navigate to **Admin → Jira Connections** to configure the first connection.\n\nIf you are running the production image locally and do not have an upstream workspace session provider, open `http://localhost:3000/admin/jira` and use **Create local admin session and continue**.\n\n### Optional local Jira stack for manual smoke testing\n\nIf you want a real Jira target on the same machine, use [docker-compose.jira.yml](docker-compose.jira.yml). It runs a single-node Jira Data Center instance plus a dedicated PostgreSQL database, isolated from the main Agile Tools stack.\n\n```bash\ndocker compose -f docker-compose.jira.yml up -d\ndocker compose -f docker-compose.jira.yml logs -f jira\n```\n\nFor a full clean rerun that also clears generated Jira seed output and the local Agile Tools admin setup state, use:\n\n```bash\nnode docker/reset-local-jira-stack.mjs\n```\n\nAfter the Jira setup wizard is complete, you can also run the optional seed job:\n\n```bash\ndocker compose -f docker-compose.jira.yml --profile bootstrap run --rm jira-bootstrap\n```\n\nSee [docs/local-jira-testing.md](docs/local-jira-testing.md) for the reset flow, browser-driven first-run wizard guidance, optional bootstrap env variables, and the minimum project, board, PAT, and permission checklist needed to connect Agile Tools to the local Jira instance.\n\n## Releases\n\nThe repository includes a tag-driven release workflow that publishes the runtime image to GitHub Container Registry and creates a GitHub Release with generated notes.\n\n### Release source of truth\n\n- Push a semver tag to publish a release: `vX.Y.Z` for stable releases or `vX.Y.Z-rc.1` for prereleases\n- Cut release tags from a green commit that is already contained in the default branch history\n- The workflow publishes `ghcr.io/\u003cowner\u003e/\u003crepo\u003e` tags for the release version and digest\n- Stable releases also receive `latest` and `X.Y`, and stable releases starting at `v1.0.0` also receive the `X` tag\n- You can also run the workflow manually from the Actions tab for an existing tag\n\n### Cut a release\n\n```bash\ngit tag v0.1.0\ngit push origin v0.1.0\n```\n\nFor prereleases, use a semver prerelease suffix such as `v0.1.0-rc.1`.\n\n### Required GitHub settings\n\n- The workflow uses the repository `GITHUB_TOKEN` to push to `ghcr.io`, so GitHub Actions package publishing must be allowed for the repository or organization\n- If a package with the same `ghcr.io/\u003cowner\u003e/\u003crepo\u003e` name was previously pushed outside Actions and is not linked to this repository, connect it to the repository or recreate it so the workflow token can write to it\n- The release workflow publishes a multi-architecture image for `linux/amd64` and `linux/arm64/v8`\n- CI validates that each published runtime shape bundles the matching Prisma schema and query engines instead of relying on runtime downloads\n\n## Developer Commands\n\n```bash\n# Install all workspace dependencies\npnpm install\n\n# Type-check all packages\npnpm typecheck\n\n# Lint all packages (zero warnings enforced)\npnpm lint\n\n# Run unit tests\npnpm test\n\n# Run contract tests\npnpm test:contract\n\n# Run integration tests (requires Docker for Testcontainers)\npnpm test:integration\n\n# Run end-to-end tests (requires a running web app)\npnpm test:e2e\n\n# Run performance benchmarks (requires Docker for Testcontainers)\npnpm test:perf\n\n# Build all packages\npnpm build\n```\n\n## Community\n\n- See [`CONTRIBUTING.md`](CONTRIBUTING.md) for local setup, validation, and pull request expectations.\n- See [`SUPPORT.md`](SUPPORT.md) for question routing and support expectations.\n- See [`SECURITY.md`](SECURITY.md) for responsible vulnerability reporting.\n- See [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) for collaboration standards.\n\n## Operator Workflow\n\nSee [`specs/001-kanban-flow-forecasting/quickstart.md`](specs/001-kanban-flow-forecasting/quickstart.md) for the full configuration and validation guide, including instructions for creating a Jira connection, defining a flow scope, triggering the first sync, and verifying all three primary user journeys.\n\n## Architecture\n\nSee [`specs/001-kanban-flow-forecasting/plan.md`](specs/001-kanban-flow-forecasting/plan.md) for the technical design, data model, and delivery strategy.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubiss-projects%2Fagile-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frubiss-projects%2Fagile-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frubiss-projects%2Fagile-tools/lists"}