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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ RUN chown -R $USERNAME:$USERNAME /workspaces
# Switch to the non-root user
USER $USERNAME

# Install Claude Code (native binary)
RUN curl -fsSL https://claude.ai/install.sh | bash

# Install bundler
RUN gem install bundler

# Set environment variables
ENV BUNDLE_PATH=/usr/local/bundle \
BUNDLE_BIN=/usr/local/bundle/bin \
GEM_HOME=/usr/local/bundle
ENV PATH=$BUNDLE_BIN:$PATH
ENV PATH=$BUNDLE_BIN:/home/$USERNAME/.local/bin:$PATH

# Default command
CMD ["/bin/bash"]
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ FROM base
COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
COPY --from=build /rails /rails

# Install Claude Code (native binary)
RUN curl -fsSL https://claude.ai/install.sh | bash && \
mv /root/.local/bin/claude /usr/local/bin/claude

# Run and own only the runtime files as a non-root user for security
RUN groupadd --system --gid 1000 rails && \
useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ GEM
bigdecimal (3.2.2)
bootsnap (1.18.6)
msgpack (~> 1.2)
brakeman (7.1.0)
brakeman (8.0.4)
racc
builder (3.3.0)
commonmarker (2.3.1)
Expand Down
9 changes: 9 additions & 0 deletions agent/.claude/hooks/on_stop_commitment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Stop hook for commitment evaluation runs.
# No-ops if the agent already called record_evaluation_run this session.

curl -s -X PATCH "${RAILS_API_URL}/api/agent/commitments/${COMMITMENT_ID}/touch_assessed" \
-H "Authorization: Bearer ${RAILS_API_KEY}" \
-H "Content-Type: application/json" \
-d "{\"reasoning\": \"Session ended without evaluation run — fallback timestamp update\"}" \
> /dev/null 2>&1 || true
8 changes: 8 additions & 0 deletions agent/.claude/hooks/on_stop_entry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
# Stop hook for entry processing runs.
# No-ops if the entry was already marked processed.

curl -s -X PATCH "${RAILS_API_URL}/api/agent/entries/${ENTRY_ID}/mark_processed" \
-H "Authorization: Bearer ${RAILS_API_KEY}" \
-H "Content-Type: application/json" \
> /dev/null 2>&1 || true
12 changes: 12 additions & 0 deletions agent/.claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"Bash(curl *)",
"WebFetch(https://*.canada.ca/*)",
"WebFetch(https://*.gc.ca/*)",
"WebFetch(https://www.parl.ca/*)",
"WebSearch"
],
"deny": []
}
}
213 changes: 150 additions & 63 deletions agent/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ These tools proxy to the existing REST API endpoints and return JSON. Tool class

## Rails API Reference

Base URL: provided in system prompt. Auth: `Authorization: Bearer <key>` (also in system prompt).
Base URL: provided in system prompt as `$RAILS_API_URL`. Auth: `Authorization: Bearer $RAILS_API_KEY` (also in system prompt).

Use `curl -s` for all API calls. Example pattern:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/commitments/1"
```

### Enum Values

Expand All @@ -30,96 +35,178 @@ Base URL: provided in system prompt. Auth: `Authorization: Bearer <key>` (also i
**commitment_event.event_type** (integer enum):
- `promised` (0), `mentioned` (1), `legislative_action` (2), `funding_allocated` (3), `status_change` (4), `criterion_assessed` (5)

**commitment_event.action_type** (integer enum, optional, prefix: `action_type_`):
**commitment_event.action_type** (integer enum, optional):
- `announcement` (0), `concrete_action` (1)

**source.source_type** (integer enum):
- `platform_document` (0), `speech_from_throne` (1), `budget` (2), `press_conference` (3), `mandate_letter` (4), `debate` (5), `other` (6), `order_in_council` (7), `treasury_board_submission` (8), `gazette_notice` (9), `committee_report` (10), `departmental_results_report` (11)

### API Endpoints (all require source_url/source_urls)

**Create commitment event** — `POST /api/agent/commitment_events`
```json
{
"commitment_id": 2354,
"event_type": "legislative_action",
"title": "Short title",
"description": "1-3 sentence blurb",
"occurred_at": "2025-07-09",
"source_url": "https://www.canada.ca/...",
"action_type": "concrete_action"
}
---

## Read Endpoints (GET)

**Commitment (full detail)** — returns criteria, matches, events, sources, departments, status_changes:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/commitments/:id"
```

**List commitments** — params: `status`, `policy_area`, `commitment_type`, `stale_days`, `government_id`, `limit`, `offset`:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/commitments?stale_days=7&limit=50"
```

**Commitment source documents** (platform, SFT, budget — use to check Budget Evidence Rule):
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/commitments/:id/sources"
```

**Assess criterion** — `PATCH /api/agent/criteria/:id`
```json
{
"new_status": "met",
"evidence_notes": "Explanation with citations",
"source_url": "https://www.canada.ca/..."
}
**Bill (with stage dates + linked commitments)**:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/bills/:id"
```

**Update commitment status** — `PATCH /api/agent/commitments/:id/status`
```json
{
"new_status": "in_progress",
"reasoning": "Clear explanation based on evidence — this is shown in the UI",
"source_urls": ["https://www.canada.ca/...", "https://gazette.gc.ca/..."],
"effective_date": "2025-07-09"
}
**List bills** — param: `parliament_number` (default 45, returns government bills only):
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/bills?parliament_number=45"
```
- `reasoning` is displayed in the UI as the status change reason — make it clear and concise (1-3 sentences)
- `effective_date` **(required)** is the date the status actually changed based on evidence (e.g., when the bill was introduced, when the program launched). Use the date of the earliest source that justifies this status. Must be YYYY-MM-DD format.

**Link bill to commitment** — `POST /api/agent/commitment_matches`
```json
{
"commitment_id": 2354,
"matchable_type": "Bill",
"matchable_id": 40,
"relevance_score": 0.9,
"relevance_reasoning": "Why this bill implements this commitment"
}

**Entry (with parsed_markdown)**:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/entries/:id"
```

**List unprocessed entries** — params: `unprocessed=true`, `government_id`, `limit`:
```bash
curl -s -H "Authorization: Bearer $RAILS_API_KEY" "$RAILS_API_URL/api/agent/entries?unprocessed=true&limit=50"
```

---

## Write Endpoints

**Fetch + register government page** — fetches URL, converts to markdown, saves as Source. Returns `source_id` and `url`. Call this BEFORE using a URL in any judgement:
```bash
curl -s -X POST "$RAILS_API_URL/api/agent/pages/fetch" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://www.canada.ca/...", "government_id": 1}'
```

**Record evaluation run** — `POST /api/agent/evaluation_runs`
```json
{
"commitment_id": 2354,
"trigger_type": "manual",
"reasoning": "Summary of evaluation",
"previous_status": "not_started",
"new_status": "in_progress",
"criteria_assessed": 8,
"evidence_found": 5
}
**Create commitment event**:
```bash
curl -s -X POST "$RAILS_API_URL/api/agent/commitment_events" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"commitment_id": 2354,
"event_type": "legislative_action",
"title": "Short title",
"description": "1-3 sentence blurb",
"occurred_at": "2025-07-09",
"source_url": "https://www.canada.ca/...",
"action_type": "concrete_action"
}'
```

**Assess criterion**:
```bash
curl -s -X PATCH "$RAILS_API_URL/api/agent/criteria/:id" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"new_status": "met",
"evidence_notes": "Explanation with citations",
"source_url": "https://www.canada.ca/..."
}'
```

**Update commitment status**:
```bash
curl -s -X PATCH "$RAILS_API_URL/api/agent/commitments/:id/status" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"new_status": "in_progress",
"reasoning": "Clear explanation based on evidence — shown in the UI",
"source_urls": ["https://www.canada.ca/..."],
"effective_date": "2025-07-09"
}'
```
- `reasoning` is displayed to users — make it clear and concise (1-3 sentences)
- `effective_date` **(required)** — the date the status actually changed based on evidence (YYYY-MM-DD)

**Link bill to commitment**:
```bash
curl -s -X POST "$RAILS_API_URL/api/agent/commitment_matches" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"commitment_id": 2354,
"matchable_type": "Bill",
"matchable_id": 40,
"relevance_score": 0.9,
"relevance_reasoning": "Why this bill implements this commitment"
}'
```

**Record evaluation run** (required at end of every evaluation — also updates `last_assessed_at`):
```bash
curl -s -X POST "$RAILS_API_URL/api/agent/evaluation_runs" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"commitment_id": 2354,
"trigger_type": "manual",
"reasoning": "Summary of what was evaluated and found",
"previous_status": "not_started",
"new_status": "in_progress",
"criteria_assessed": 8,
"evidence_found": 5
}'
```

---

## Fetching Government Pages

Use the **WebFetch tool** to read official page content (allowed domains: `*.canada.ca`, `*.gc.ca`, `www.parl.ca`).

Then register the page as a Source:
```bash
curl -s -X POST "$RAILS_API_URL/api/agent/pages/fetch" \
-H "Authorization: Bearer $RAILS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://...", "government_id": 1}'
```

You MUST register a page before referencing its URL in any judgement (assess_criterion, create_commitment_event, update_commitment_status).

---

## Database Schema (key tables)

**commitments**: id, government_id, title, description, original_text, commitment_type (enum), status (enum), date_promised, target_date, last_assessed_at, policy_area_id, metadata (jsonb)
**commitments**: id, government_id, title, description, original_text, commitment_type (enum), status (enum), date_promised, target_date, last_assessed_at, policy_area_id

**criteria**: id, commitment_id, category (enum), description, verification_method, status (enum), evidence_notes, assessed_at, position

**criterion_assessments**: id, criterion_id, previous_status, new_status, source_id, evidence_notes, assessed_at

**commitment_matches**: id, commitment_id, matchable_type, matchable_id, relevance_score, relevance_reasoning, matched_at, assessed

**commitment_events**: id, commitment_id, source_id, event_type (enum), action_type (enum), title, description, occurred_at, metadata (jsonb)
**commitment_events**: id, commitment_id, source_id, event_type (enum), action_type (enum), title, description, occurred_at

**bills**: id, bill_id, bill_number_formatted, parliament_number, short_title, long_title, latest_activity, passed_house_first_reading_at, passed_house_second_reading_at, passed_house_third_reading_at, passed_senate_first_reading_at, passed_senate_second_reading_at, passed_senate_third_reading_at, received_royal_assent_at

**sources**: id, government_id, source_type (enum), title, url, date

**entries**: id, feed_id, title, published_at, url, scraped_at, parsed_markdown, activities_extracted_at, government_id
**entries**: id, feed_id, title, published_at, url, scraped_at, parsed_markdown, agent_processed_at, government_id

**governments**: id=1 is the current Carney government (45th Parliament)

---

## Rules

- Do NOT use Read, Glob, Grep, or filesystem tools to explore the Rails codebase. Everything you need is above.
- Use the MCP tools (get_commitment, get_bill, etc.) for reading data.
- Use curl via Bash for ALL write operations.
- Every judgement (assess_criterion, create_commitment_event, update_commitment_status) MUST include a source_url that was previously fetched via fetch_government_page.
- Fetch pages BEFORE referencing them. The fetch auto-registers them as Sources in the DB.
- Do NOT use Read, Glob, Grep, or filesystem tools. Everything you need is above.
- Use `curl -s` via Bash for ALL API calls (reads and writes).
- Use WebFetch for reading government page content (canada.ca / gc.ca / parl.ca only).
- Every judgement (assess_criterion, create_commitment_event, update_commitment_status) MUST include a source_url that was first registered via `POST /api/agent/pages/fetch`.
- Always call `POST /api/agent/evaluation_runs` at the end of every commitment evaluation with a reasoning summary.
27 changes: 0 additions & 27 deletions agent/pyproject.toml

This file was deleted.

Empty file removed agent/src/agent/__init__.py
Empty file.
37 changes: 0 additions & 37 deletions agent/src/agent/db.py

This file was deleted.

Empty file removed agent/src/agent/domain/__init__.py
Empty file.
Loading
Loading