Integrate OAuth, Jira export, CI, and UX fixes - #10
Merged
Merged
Conversation
…hem below") The shared run_party() consensus message told users to "Review them below.", which is wrong for the moonshot flow where changes are auto-applied. Add an auto_apply flag (passed from run_moonshot) that switches the wording to "applied automatically." while leaving the normal review flow unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
…old start The OpenAI backend now reports status="loading" on the first call / model switch so the UI can surface a cold-start state. The first model call of a run (_infer_calibration) is wrapped in the heartbeat (_with_status) so the status bar shows the "loading model" graphic instead of nothing, and the status bars render an immediate cold-start graphic on first paint. Co-authored-by: Cursor <cursoragent@cursor.com>
Pure identifier rename of all PROMPTGEN_* environment variables to INCIPIT_*; defaults and behavior unchanged. Lowercase logger names and the .promptgen.json default filename are intentionally left as-is. Co-authored-by: Cursor <cursoragent@cursor.com>
Drive app/repo.py's _github() and _firecrawl() against a respx-mocked httpx transport (no DNS/sockets): the full GitHub meta->languages->readme->tree->_summarize path, graceful degradation when the follow-up calls fail, bearer-token header, and the private-repo->Firecrawl fallback (with and without FIRECRAWL_URL configured). Adds respx to requirements-dev.txt. Co-authored-by: Cursor <cursoragent@cursor.com>
Per-user "Login with GitHub" so the wizard can ground specs in private repos. The access token is stored server-side only (new app/auth.py in-memory store, TTL-swept like the session store); the browser cookie carries just a signed, opaque session id (itsdangerous) set HttpOnly + Secure + SameSite=Strict. - app/auth.py: per-provider token + CSRF-state store keyed by opaque session id (generalized so the Atlassian login can reuse it). - app/audit.py: append-only promptgen.audit log of token issuance/revocation with timestamp + provider account id (no tokens logged). - app/config.py: INCIPIT_GITHUB_OAUTH_CLIENT_ID/SECRET/REDIRECT_URL/SCOPES, INCIPIT_SESSION_COOKIE_SECRET, INCIPIT_COOKIE_SECURE (default true). - app/main.py: GET /auth/github/login, GET /auth/github/callback, POST /auth/github/logout + signed-cookie helpers. - itsdangerous dependency; .env.example + README documented. - tests/test_auth.py: login redirect, cookie flags, code exchange storing the token server-side, CSRF-state validation, logout clearing the cookie, audit. Co-authored-by: Cursor <cursoragent@cursor.com>
Let a signed-in user pick their private repos to ground the spec in. - app/repo.py: list_private_repos(token) — paginated GET /user/repos (visibility=private), Bearer auth, 3000ms timeout, retry-once + 1500ms backoff, typed GitHubAuthError on 401. fetch_selected_repo_context() reuses _github() with the per-user token (best-effort). - app/main.py: GET /api/github/repos — requires a valid auth cookie (else 401), renders a searchable multi-select; GitHubAuthError -> 401 re-authorize modal. create_session/moonshot copy the picked repos + token onto the session. - app/wizard/state.py: selected_repos + github_token (server-side only). - app/wizard/flow.py: _ensure_repo_context fetches + concatenates the selected private repos and the repo_url fallback, token-budgeted by REPO_CONTEXT_MAX. - UI: "Login with GitHub" button + repo checklist with client-side filter in step1 #repo-row, github_error re-authorize modal (.history-modal styling), and an htmx:beforeSwap handler so 401s render the modal. - tests/test_github_repos.py: repos JSON shape, pagination, 401->GitHubAuthError, retry/backoff, route authed vs 401 (no token leakage), grounding + budget. Co-authored-by: Cursor <cursoragent@cursor.com>
Mirror the GitHub login on the shared server-side auth store (app/auth.py) and audit log (app/audit.py): the Atlassian access + refresh tokens, expiry, and resolved cloudId/site live server-side only; the browser keeps just the opaque signed session id. - config: INCIPIT_ATLASSIAN_OAUTH_CLIENT_ID/SECRET/REDIRECT_URL/SCOPES - routes: GET /auth/atlassian/login (audience=api.atlassian.com, response_type=code, prompt=consent, CSRF state, scope + offline_access), GET /auth/atlassian/callback (token exchange + accessible-resources → cache cloudId + site), POST /auth/atlassian/logout (revoke + audit) - refresh_atlassian_token(): refresh via refresh_token near/after expiry, raising AtlassianAuthError on failure for the re-authorize path - UI: "Sign in with Atlassian" button + connected-site chip on step6_final - tests: login redirect (offline_access + state), callback stores token/cloudId with hardened cookie, refresh + failure paths, logout Also add respx to requirements-dev.txt (the OAuth tests mock httpx). Co-authored-by: Cursor <cursoragent@cursor.com>
Add a per-user "Export to Jira" flow on the final step, built on the Atlassian OAuth login: - app/jira.py: pure Jira Cloud REST v3 client (Bearer user token) — list projects (project/search), create issue (ADF description), upload the raw .md attachment (X-Atlassian-Token: no-check multipart), browse URL helper - app/markdown_adf.py: markdown → Atlassian Document Format (headings, paragraphs, bold/italic, inline + fenced code, bullet/ordered lists, links) - config: INCIPIT_JIRA_ISSUE_TYPES, INCIPIT_JIRA_DEFAULT_PROJECT_KEY, INCIPIT_JIRA_EXPORT_TIMEOUT (ms budget) - routes: GET /api/jira/projects (refresh token, 401 → re-authorize modal), POST /api/jira/export (assemble_final → ADF → create issue → attach .md → audit) enforced inside the time budget with a clear overrun message - UI: project + issue-type dropdowns, Export button + spinner, confirmation bubble with the issue key + link; 401 beforeSwap handler for the modal - audit.jira_export() append-only record - tests: REST client (projects/create/attach), routes (authed, 401, budget, partial-attachment), and markdown→ADF unit tests Co-authored-by: Cursor <cursoragent@cursor.com>
- pytest.ini: enable pytest-cov with a 90% --cov-fail-under gate, scoped to the security-critical, fully-offline-testable modules (app/auth.py, app/audit.py, app/jira.py, app/markdown_adf.py). A 90% gate over all of `app` is impractical because the LLM/diffusion backends and wizard orchestration call out to a model/subprocess and aren't exercised offline; the auth/export routes live in app/main.py (alongside every wizard route, so they can't be isolated per-file) but are covered by the OAuth/export tests. - requirements-dev.txt: pin pytest-cov==7.1.0 (respx already added). - .github/workflows/ci.yml: Python 3.11, install deps, run pytest + coverage. - README: consolidated INCIPIT_* env-var reference table (covers the GitHub + Atlassian OAuth and Jira export vars so a Doppler config can be populated end-to-end), a "Sign in with Atlassian (Jira export)" usage section, and a "Testing & coverage" section documenting the scoping. - tests: auth-store edge-case tests (lifts app/auth.py to 100%). Scoped modules report 100% coverage; full suite is 177 passed, 1 xfailed. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # app/main.py # requirements-dev.txt
Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # app/main.py # app/templates/base.html
SameSite=Strict prevented the signed session cookie from being sent on the top-level cross-site redirect back from github.com / auth.atlassian.com to /auth/*/callback, so the server could not recover the session id to validate the OAuth `state` (login failed with "invalid/expired state"). Lax is sent on top-level cross-site GET navigations while still being withheld from cross-site subrequests; the access token remains HttpOnly + server-side regardless. Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve GitHub/Jira integration styles and OAuth handling while adopting the Consermo UI refresh. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep provider credentials lifecycle-safe, prevent ambiguous Jira retries, and preserve reusable WebUI configuration aliases. Co-authored-by: Cursor <cursoragent@cursor.com>
Scope the repository-picker request to its own element so HTMX does not replace the step-one form. Co-authored-by: Cursor <cursoragent@cursor.com>
…target Fix GitHub repository picker wizard replacement
Return users to their originating brief with a retry message when Atlassian cannot complete authorization, and surface sign-in in the fixed action bar. Co-authored-by: Cursor <cursoragent@cursor.com>
…allback Fix Atlassian sign-in callback recovery
| cookie).""" | ||
| _sweep() | ||
| rec = AuthRecord(id=uuid.uuid4().hex, created=time.time()) | ||
| _auths[rec.id] = rec |
There was a problem hiding this comment.
🔒 Agentic Security Review
Severity: MEDIUM
OAuth auth records are stored in a process-global dictionary with TTL-only cleanup and no hard size limit. The login endpoints can allocate new records for unauthenticated requests, so repeated requests can grow memory usage without bound within the TTL window.
Impact: low-cost unauthenticated memory pressure can degrade service or cause denial of service.
Reviewed by Cursor Security Reviewer for commit bc79b5e. Configure here.
…loy/dev-integration Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # app/main.py # app/templates/step1_idea.html
readwrightexecute
added a commit
to Suraj-Bangade/incipit
that referenced
this pull request
Aug 3, 2026
…ayout Re-home the PR readwrightexecute#10 work under the webapp/ split: new auth/audit/jira/ADF modules, templates, and tests move to webapp/, the coverage gate lands in webapp/pytest.ini, CI runs the webapp and skills suites as separate jobs, and the GitHub/Atlassian/env-var docs move into webapp/README.md. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.




Summary
Integration branch bundling the OAuth, Jira export, CI, and repo-fetch work that was developed on feature branches, plus two small UX fixes and a sync with current
master..mdattachment)INCIPIT_env-var prefix (wasPROMPTGEN_)SameSite=Laxsession cookie so OAuth callbacks workTest plan
Made with Cursor