mirror of
https://github.com/we-promise/sure.git
synced 2026-08-04 16:12:14 +00:00
Three findings from the automated review passes, all confirmed against the code before changing anything. The download URL was dead on arrival for the caller it was built for. Sure serves stored files through Active Storage controllers that config/initializers/active_storage_authorization.rb gates on `viewable_by?(Current.user)` — a signed-in browser session. An MCP client has a bearer token and no session, so following the URL would have redirected to sign-in. Removed it rather than leaving a link that cannot work, and the description now points at search_family_files or the vault UI. Coverage called a month `covered` when a document merely existed. An unreconciled statement is not mismatched, so it took the `covered` branch, and the payload carried nothing to correct the reading — the same "advertised verification that never happened" bug fixed last round in get_account_statement, in a second place. Months now carry their own reconciliation_status, and the description says covered means presence, not agreement. Listing filtered visibility after limiting. Beyond underfilling a page, with no cursor and a 100-row cap an accessible statement behind enough newer invisible ones was unreachable. Visibility now lives in the query, mirroring viewable_by? for a statement manager. Also: rescue unexpected upload failures into a tool error instead of a raw exception string, derive the documented size limit from MAX_FILE_SIZE, list every coverage status in mcp.md, and cover the failed-reconciliation and base64-normalisation branches. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JFDp9HhXDeswadu4cxFojn
118 lines
4.3 KiB
Ruby
118 lines
4.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class Assistant::Function::GetStatementCoverage < Assistant::Function
|
|
include Assistant::Function::StatementVaultSupport
|
|
|
|
class << self
|
|
def name
|
|
"get_statement_coverage"
|
|
end
|
|
|
|
def description
|
|
<<~INSTRUCTIONS
|
|
Report, month by month, which statements the family actually holds for an
|
|
account in a given year — the document-coverage map behind the numbers.
|
|
|
|
Each month comes back with one status:
|
|
|
|
- `covered` — a linked statement covers the month. This means a DOCUMENT
|
|
EXISTS, not that it agrees with the ledger: most statements have no
|
|
balances entered, so there is nothing to reconcile and they still count
|
|
as covered. Check `reconciliation_status` on the month before saying a
|
|
month is verified.
|
|
- `mismatched` — a statement covers it, but its balances disagree with the ledger
|
|
- `missing` — no statement on record; the month's figures have no document behind them
|
|
- `ambiguous` — a statement was suggested for this account but nobody has confirmed the link
|
|
- `duplicate` — two or more linked statements overlap the same month
|
|
- `not_expected` — outside the account's expected statement range
|
|
|
|
Each covered month also carries `reconciliation_status`: `matched` when
|
|
every statement in it reconciles against the ledger, `mismatched` when one
|
|
disagrees, and `unavailable` when nobody has entered the balances — which
|
|
is the common case.
|
|
|
|
Use it before asserting anything about a period: "no statement on record"
|
|
is a legitimate and necessary answer, and is very different from "the
|
|
balance was zero". Use it to tell the user exactly which documents to go
|
|
find.
|
|
|
|
Example:
|
|
|
|
```
|
|
get_statement_coverage({ account_id: "abc123-def456", year: 2026 })
|
|
```
|
|
INSTRUCTIONS
|
|
end
|
|
end
|
|
|
|
def strict_mode?
|
|
false
|
|
end
|
|
|
|
def params_schema
|
|
build_schema(
|
|
required: [ "account_id" ],
|
|
properties: {
|
|
account_id: {
|
|
type: "string",
|
|
description: "UUID of the account to report coverage for."
|
|
},
|
|
year: {
|
|
type: "integer",
|
|
description: "Calendar year. Defaults to the most recent year with expected statements."
|
|
}
|
|
}
|
|
)
|
|
end
|
|
|
|
def call(params = {})
|
|
return not_a_statement_manager unless statement_manager?
|
|
|
|
account_id = params["account_id"].to_s
|
|
return error("invalid_account_id", "account_id must be a UUID.") unless valid_uuid?(account_id)
|
|
|
|
# accessible_by, not writable_by, is deliberate: this reports which documents
|
|
# exist for an account and writes nothing. Someone with read access to an
|
|
# account is entitled to know which of its statements are on file. Do not
|
|
# "tighten" this to writable_by — that would hide gaps from the people who
|
|
# can see the figures those gaps sit behind.
|
|
account = family.accounts.accessible_by(user).find_by(id: account_id)
|
|
return error("account_not_found", "No accessible account found with that ID.") unless account
|
|
|
|
coverage = AccountStatement::Coverage.for_year(account, params["year"])
|
|
|
|
{
|
|
success: true,
|
|
account: account_ref(account),
|
|
year: coverage.selected_year,
|
|
available_years: coverage.available_years,
|
|
summary: coverage.summary_counts,
|
|
months: coverage.months.map { |month| month_payload(month) }
|
|
}
|
|
end
|
|
|
|
private
|
|
def month_payload(month)
|
|
{
|
|
month: month.date.strftime("%Y-%m"),
|
|
status: month.status,
|
|
# A month is "covered" on document presence alone — an unreconciled
|
|
# statement is not mismatched, so it lands in `covered`. Without this
|
|
# field an agent cannot tell "the ledger agrees" from "nobody checked".
|
|
reconciliation_status: reconciliation_status_for(month),
|
|
statement_ids: month.statements.map(&:id),
|
|
unconfirmed_statement_ids: month.ambiguous_statements.map(&:id)
|
|
}.compact_blank
|
|
end
|
|
|
|
def reconciliation_status_for(month)
|
|
return nil if month.statements.empty?
|
|
|
|
statuses = month.statements.map(&:reconciliation_status).uniq
|
|
return "mismatched" if statuses.include?("mismatched")
|
|
return "unavailable" if statuses.include?("unavailable")
|
|
|
|
"matched"
|
|
end
|
|
end
|