Skip to content

Integrate OAuth, Jira export, CI, and UX fixes - #10

Merged
readwrightexecute merged 26 commits into
masterfrom
deploy/dev-integration
Aug 3, 2026
Merged

readwrightexecute merged 26 commits into
masterfrom
deploy/dev-integration

Conversation

@readwrightexecute

Copy link
Copy Markdown
Owner

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.

  • GitHub OAuth — sign-in with GitHub and server-side token store for private repo access
  • GitHub private repos — multi-select and grounding against selected private repositories
  • Atlassian OAuth — "Sign in with Atlassian" (Jira OAuth 2.0 / 3LO)
  • Jira export — export the mega-prompt to Jira over REST (ADF body + .md attachment)
  • CI — GitHub Actions workflow with scoped coverage gate and env-var reference
  • Tests — mocked-HTTP end-to-end tests for repo fetch
  • RenameINCIPIT_ env-var prefix (was PROMPTGEN_)
  • Auth fixSameSite=Lax session cookie so OAuth callbacks work
  • UX fixes — cold-start loading graphic during model switch; moonshot consensus copy says changes were auto-applied
  • Synced with master — agent skills packaging, party answer persistence, model-list key fallback, race/XSS hardening

Test plan

  • GitHub OAuth sign-in and callback complete successfully
  • Private repo multi-select loads and grounds correctly
  • Atlassian OAuth sign-in and callback complete successfully
  • Jira export creates issue with ADF body and markdown attachment
  • CI workflow passes on this branch
  • Cold-start model switch shows loading graphic
  • Moonshot consensus step shows auto-applied messaging
  • Existing wizard flow (clarify → sections → final) still works end-to-end

Made with Cursor

readwrightexecute and others added 25 commits June 24, 2026 15:22
…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

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Found 2 net-new security issues after deduplication and triage.

Open in Web View Automation 

Sent by Cursor Security Agent: Security Reviewer (1)

Comment thread app/main.py Outdated
Comment thread app/auth.py
cookie)."""
_sweep()
rec = AuthRecord(id=uuid.uuid4().hex, created=time.time())
_auths[rec.id] = rec

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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.

Fix in Cursor Fix in Web

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
readwrightexecute merged commit d39b45d into master Aug 3, 2026
3 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant