Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
79f0379
feat(spa): add route-driven flow and session recovery
gildesmarais Apr 5, 2026
3d1a4f4
feat(spa): add explicit workflow states and error taxonomy
gildesmarais Apr 5, 2026
43fb645
test(spa): expand route smoke coverage
gildesmarais Apr 5, 2026
1b9b968
chore(spa): finalize router polish and gate fixes
gildesmarais Apr 5, 2026
a9d3bc8
test(e2e): align token-route history navigation assertion
gildesmarais Apr 5, 2026
4569f92
security(frontend): harden CSP and session token handling
gildesmarais Apr 5, 2026
ce3bbb5
docs: remove preact-router planning note
gildesmarais Apr 5, 2026
c90405f
dev-routing: make 4000 API-only and keep SPA on 4001
gildesmarais Apr 5, 2026
e61cf60
Simplify create UX with auto strategy and resilient retry recovery
gildesmarais Apr 5, 2026
b43296b
Improve mobile footer flow and degraded-feed messaging
gildesmarais Apr 5, 2026
4a02ce4
Harden create-flow retry recovery with explicit retry actions
gildesmarais Apr 5, 2026
763763a
Add DSN-gated Sentry breadcrumbs and triage runbook
gildesmarais Apr 5, 2026
a5e2f8d
Move retry classification upstream
gildesmarais Apr 5, 2026
c55fc98
Make empty extraction a 422 failure and harden bookmarklet routing
gildesmarais Apr 5, 2026
c2020a7
feat(api): structured conversion failure and feed status contracts
gildesmarais Apr 5, 2026
37f56c6
refactor(frontend): consume structured metadata and remove flow heuri…
gildesmarais Apr 5, 2026
6362590
chore(api): regenerate openapi and frontend generated client
gildesmarais Apr 5, 2026
4505466
fix(build): make rubocop invocation deterministic in make ready
gildesmarais Apr 5, 2026
9ca2534
Fix OpenAPI status path determinism and smoke contract
gildesmarais Apr 5, 2026
2929de2
Harden conversion hydration and snapshot restore handling
gildesmarais Apr 5, 2026
7b25330
Reorder footer actions and add CI parity gate guidance
gildesmarais Apr 5, 2026
cdfadfb
fix PR review findings for status contract and storage utils
gildesmarais Apr 5, 2026
c2160e5
Align backend with html2rss auto fallback
gildesmarais Apr 24, 2026
1ef42f4
Remove strategy-era frontend contract handling
gildesmarais Apr 24, 2026
787fe4e
Run Playwright smoke with devcontainer Chromium
gildesmarais Apr 24, 2026
c7dc88c
Align result journey states
gildesmarais Apr 24, 2026
97a42db
feat: remove backend readiness/status scaffolding
gildesmarais Apr 25, 2026
46af096
feat: make readiness/preview checks frontend-owned
gildesmarais Apr 25, 2026
fccefaa
Refactor feed creation and token internals
gildesmarais Apr 25, 2026
20b28e3
Polish frontend utility actions
gildesmarais Apr 25, 2026
26a26fd
Keep API error contracts in development
gildesmarais Apr 25, 2026
6e4f9bb
Simplify result display preview and cleanup result UI
gildesmarais Apr 29, 2026
5809de0
chore(deps): update pnpm
gildesmarais Apr 29, 2026
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
4 changes: 4 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ SHELL ["/bin/sh", "-o", "pipefail", "-c"]
RUN apk add --no-cache \
bash \
build-base \
chromium \
curl \
git \
harfbuzz \
libxml2-dev \
libxslt-dev \
nodejs \
nss \
npm \
openssl-dev \
python3 \
ttf-freefont \
yaml-dev \
tzdata

Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This document defines execution constraints for AI agents. For general contribut
## Agent-Specific Verification Rules

- Always run Dev Container smoke + `make ready` for changes.
- For frontend changes or API contract/spec changes, run `make ci-ready` to mirror CI parity checks.
- For frontend changes, also verify in `chrome-devtools` MCP at `http://127.0.0.1:4001/` while the Dev Container is running.
- Capture a quick state check for all affected UI states (e.g., guest/member/result) to enforce state parity and avoid duplicate actions.

Expand Down
6 changes: 3 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'html2rss', '~> 0.18'
# gem 'html2rss', github: 'html2rss/html2rss', branch: :master
# gem 'html2rss', '~> 0.18'
gem 'html2rss', github: 'html2rss/html2rss', branch: 'codex-pr-auto-fallback-pipeline'
gem 'html2rss-configs', github: 'html2rss/html2rss-configs'

# Use these instead of the two above (uncomment them) when developing locally:
# gem 'html2rss', path: '../html2rss'
# gem 'html2rss-configs', path: '../html2rss-configs'

gem 'concurrent-ruby'
gem 'parallel'
gem 'rack-cache'
gem 'rack-timeout'
Expand All @@ -21,7 +22,6 @@ gem 'zeitwerk'
gem 'puma', require: false

group :development do
gem 'byebug'
gem 'irb', require: false
gem 'rake', require: false
gem 'rubocop', require: false
Expand Down
143 changes: 73 additions & 70 deletions Gemfile.lock

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

.PHONY: help test lint lint-js lint-ruby lintfix lintfix-js lintfix-ruby setup dev clean frontend-setup check-frontend quick-check ready yard-verify-public-docs openapi openapi-verify openapi-client openapi-client-verify openapi-lint openapi-lint-redocly openapi-lint-spectral openai-lint-spectral test-frontend-e2e
.PHONY: help test lint lint-js lint-ruby lintfix lintfix-js lintfix-ruby setup dev clean frontend-setup check-frontend quick-check ready ci-ready yard-verify-public-docs openapi openapi-verify openapi-client openapi-client-verify openapi-lint openapi-lint-redocly openapi-lint-spectral openai-lint-spectral test-frontend-e2e

RUBOCOP_FLAGS ?= --cache false

# Default target
help: ## Show this help message
Expand Down Expand Up @@ -60,7 +62,7 @@ lint: lint-ruby lint-js ## Run all linters (Ruby + Frontend) - errors when issue

lint-ruby: ## Run Ruby linter (RuboCop) - errors when issues found
@echo "Running RuboCop linting..."
bundle exec rubocop
bundle exec rubocop $(RUBOCOP_FLAGS)
@echo "Running Zeitwerk eager-load check..."
bundle exec rake zeitwerk:verify
@echo "Running YARD public-method docs check..."
Expand Down Expand Up @@ -105,6 +107,13 @@ ready: ## Pre-commit gate (quick checks + RSpec)
bundle exec rspec
@echo "Pre-commit checks complete!"

ci-ready: ## CI parity gate (ready + OpenAPI verify + frontend e2e smoke)
@echo "Running CI parity checks..."
$(MAKE) ready
$(MAKE) openapi-verify
$(MAKE) test-frontend-e2e
@echo "CI parity checks complete!"

yard-verify-public-docs: ## Verify essential YARD docs for all public methods in app/
bundle exec rake yard:verify_public_docs

Expand Down
49 changes: 30 additions & 19 deletions app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@

module Html2rss
module Web
DEFAULT_HEADERS = {
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1; mode=block',
'X-Frame-Options' => 'SAMEORIGIN',
'X-Permitted-Cross-Domain-Policies' => 'none',
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains; preload',
'Cross-Origin-Embedder-Policy' => 'require-corp',
'Cross-Origin-Opener-Policy' => 'same-origin',
'Cross-Origin-Resource-Policy' => 'same-origin',
'X-DNS-Prefetch-Control' => 'off',
'X-Download-Options' => 'noopen'
}.freeze

##
# Roda app serving RSS feeds via html2rss
class App < Roda
Expand All @@ -32,7 +47,8 @@ class App < Roda
</html>
HTML
FRONTEND_DIST_PATH = 'frontend/dist'
FRONTEND_INDEX_PATH = File.join(FRONTEND_DIST_PATH, 'index.html')
FRONTEND_DIST_INDEX_PATH = File.join(FRONTEND_DIST_PATH, 'index.html')
FRONTEND_SOURCE_INDEX_PATH = 'frontend/index.html'
def self.development? = EnvironmentValidator.development?

def development? = self.class.development?
Expand All @@ -43,7 +59,7 @@ def development? = self.class.development?

plugin :content_security_policy do |csp|
csp.default_src :none
csp.style_src :self, "'unsafe-inline'"
csp.style_src :self
csp.script_src :self
csp.connect_src :self
csp.img_src :self
Expand All @@ -65,21 +81,7 @@ def development? = self.class.development?
csp.block_all_mixed_content
csp.upgrade_insecure_requests
end

plugin :default_headers, {
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1; mode=block',
'X-Frame-Options' => 'SAMEORIGIN',
'X-Permitted-Cross-Domain-Policies' => 'none',
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'geolocation=(), microphone=(), camera=()',
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains; preload',
'Cross-Origin-Embedder-Policy' => 'require-corp',
'Cross-Origin-Opener-Policy' => 'same-origin',
'Cross-Origin-Resource-Policy' => 'same-origin',
'X-DNS-Prefetch-Control' => 'off',
'X-Download-Options' => 'noopen'
}
plugin :default_headers, DEFAULT_HEADERS

plugin :json_parser
plugin :static,
Expand All @@ -91,7 +93,7 @@ def development? = self.class.development?
plugin :not_allowed
plugin :exception_page
plugin :error_handler do |error|
next exception_page(error) if development?
next exception_page(error) if development? && !error.is_a?(HttpError)

ErrorResponder.respond(request: request, response: response, error: error)
end
Expand All @@ -107,7 +109,16 @@ def development? = self.class.development?

def render_index_page(router)
router.response['Content-Type'] = 'text/html'
File.exist?(FRONTEND_INDEX_PATH) ? File.read(FRONTEND_INDEX_PATH) : FALLBACK_HTML
index_path = index_page_path
return File.read(index_path) if index_path

FALLBACK_HTML
end

def index_page_path
return FRONTEND_DIST_INDEX_PATH if File.exist?(FRONTEND_DIST_INDEX_PATH)

FRONTEND_SOURCE_INDEX_PATH if development? && File.exist?(FRONTEND_SOURCE_INDEX_PATH)
end
end
end
Expand Down
22 changes: 0 additions & 22 deletions app/web/api/v1/contract.rb

This file was deleted.

Loading
Loading