Skip to content

feat(proposer): keel assets propose — screen an LLM asset shortlist, admit nothing - #153

Merged
eaitbrahim merged 5 commits into
mainfrom
feat/asset-proposer
Jul 25, 2026
Merged

feat(proposer): keel assets propose — screen an LLM asset shortlist, admit nothing#153
eaitbrahim merged 5 commits into
mainfrom
feat/asset-proposer

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

What

Implements roadmap direction #3, slice (A): keel assets propose --from <shortlist.json> — a read-only candidate source that ingests an externally-produced LLM asset shortlist and routes each candidate through the existing, unmodified _screen_product admission gate.

Follows docs/superpowers/specs/2026-07-24-llm-asset-proposer-design.md and docs/superpowers/plans/2026-07-24-llm-asset-proposer.md.

Design — outside-first hybrid

The LLM + web-search scouting happens outside keel (operator / Claude + firecrawl skills produce the JSON shortlist). keel owns only the deterministic, testable half: validate → screen → report.

  • New pure module keel/proposer.pyparse_proposal (schema + per-candidate validation), build_proposal_report (routes each candidate through an injected screen_fn), render_proposal_report (human) + report_to_jsonable (--json). It does not import keel.cli (no cycle); the CLI passes the real _screen_product as the gate.
  • Thin assets propose command in cli.py, beside assets screen / assets holdings.

Guarantees (verified by an independent review — by construction, not just by test)

  • Read-only / admits nothing. No INSERT/UPDATE/DELETE, no attestation write, no allowlist mutation, no new Repository method. screen_asset / ScreenPolicy / _screen_product are byte-unchanged.
  • Same gate, by construction. assets propose and assets screen return the identical verdict for the same asset (test asserts ADMIT on both sides). Because attestation=None fails closed, a freshly-scouted asset REJECTs until a human attests it and data is fetched — the proposer surfaces candidates, it never admits them.
  • Provenance enforced by code. A candidate with zero source citations is rejected at schema validation, never screened.
  • shariah_hypothesis can't influence admission. build_proposal_report passes only (repo, product, quote) to the gate; the hypothesis is rendered as an explicit UNVERIFIED (never used for admission) hint and can never become an attestation.

Out of scope (per spec)

No embedded LLM/web API, no new dependency, no second secret, no proposal-audit table, no allowlist mutation. Strategy-proposal (B) and insights/veto (C) are separate future specs.

Tests

+29 tests (1629 → 1658), ruff clean. keel/proposer.py unit tests (schema, builder, renderers, JSON) + CLI integration tests (same-gate equivalence, admits-nothing [reopens the DB], --json validity, hypothesis-never-admits, citation-required, hyphenated-asset rejection, non-dict entry, disclaimer on human path).

Review

Built TDD (Sonnet) from the plan, independently reviewed by a fresh-context Opus agent → mergeable-as-is. The 4 hardening items it raised are included: single-sourced DATA_DERIVED_FAILURES in screen.py (with a pin test so a tag rename breaks a test rather than silently disabling suppression), alphanumeric-asset validation, a stronger writes-nothing test, and two coverage tests.

🤖 Generated with Claude Code

eaitbrahim and others added 5 commits July 25, 2026 00:04
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…steps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… nothing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hyphenated-asset rejection, stronger writes-nothing test

- Single source of truth for the zero-bar failure-tag suppression: add
  DATA_DERIVED_FAILURES to keel/compliance/screen.py (it owns the tags) and have
  both keel/cli.py's `_DATA_DERIVED_FAILURES` and keel/proposer.py import it
  instead of each defining its own copy, plus a pin test asserting the set
  matches screen_asset's real output.
- Reject an asset code containing non-alphanumeric characters (e.g. "sol-usd")
  at schema validation: `_history_product` and the gate key off
  product.split("-")[0], but the report's on_allowlist/attested labels keyed off
  the full string would silently disagree with what was actually screened.
- Strengthen test_propose_writes_nothing to assert the full
  repo.get_asset_attestations() == [] on a freshly reopened DB handle, so a
  stray write to any asset/table would be caught, not just the one candidate
  proposed.
- Add coverage: human output includes the DISCLAIMER footer; a non-dict
  candidate entry lands in `invalid` with a clear reason instead of crashing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@eaitbrahim
eaitbrahim merged commit b232c99 into main Jul 25, 2026
1 check passed
@eaitbrahim
eaitbrahim deleted the feat/asset-proposer branch July 25, 2026 04:33
@eaitbrahim eaitbrahim added the feature New capability (groups under Features) label Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New capability (groups under Features)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant