From gw
Deep multi-method verification of any entity cited in a grant proposal — organisations, people, projects, products, standards, certifications, datasets, clinical trials, regulatory pathways. Combines SPA/JS-bundle inspection, Wayback Machine fallback, transliteration variants, country-specific company registries, national grant databases beyond CORDIS, GitHub org enumeration, ORCID/Scholar credential verification, WHOIS/DNS provenance, and mm-ask multi-model consensus tier assignment. Catches the three canonical failure modes that ordinary WebFetch + CORDIS search misses — JavaScript SPAs that hide content, non-Latin name transliteration mismatches, and wrong-database fabrication flags. Default pipeline invocation verifies consortium partners; can be invoked ad-hoc by any other skill for any entity type.
How this skill is triggered — by the user, by Claude, or both
Slash command
/gw:deep-verifyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are running deep multi-method verification on entities cited in a grant proposal. Your job is to find evidence that defends or contradicts each claim the proposal makes about an external entity, then assign a credibility tier and produce a verified description that can be pasted into the proposal verbatim.
You are running deep multi-method verification on entities cited in a grant proposal. Your job is to find evidence that defends or contradicts each claim the proposal makes about an external entity, then assign a credibility tier and produce a verified description that can be pasted into the proposal verbatim.
This skill exists because three canonical failure modes of basic web search and CORDIS lookup have killed proposal claims in the past:
The SPA-blind failure. A previous research agent declared NOETIV (a Greek health-AI company) "near-fatal placeholder" because WebFetch noetiv.com returned only <title>Noetiv – Healthcare Reimagined</title> <div id="root"></div>. The agent saw an empty div and concluded "no content". The reality: noetiv.com is a React Single-Page Application — the actual content lives in a 2.53 MB JavaScript bundle (/assets/index-Bsq5Or3p.js) which, when inspected directly, contains a live skin-analysis platform, an OAuth server, six team members including Vassilios Verykios (Hellenic Open University, 170+ papers, 10,100+ citations), the Egg Accelerator 2025 cohort, and pricing tiers. The skill must NOT repeat this mistake. Always inspect the JS bundle before concluding a website is empty.
The transliteration-mismatch failure. The same agent declared "Yiorgos Bakalis" (a NOETIV CFO) "no professional profile found anywhere" — because it searched for the literal Latin spelling. The reality: Yiorgos Bakalis is the romanisation variant of Γιώργος Μπακέλλας, who is George Bakellas — a 25-year Sanofi finance executive with a clear LinkedIn profile and CFO role at NOETIV. The skill must always try transliteration variants for non-Latin names.
The wrong-database failure. The same agent declared AI STM Learning's "€1.7M EU grant" claim "unverified, not in CORDIS, possibly fabricated". The reality: the grant is a Romanian POCIDIF/PCIDIF programme award (Contract 390016/28.08.2025, SMIS code 334596, ERDF co-financing) — a national operational programme using EU structural funds, tracked in Romania's MySMIS2021 system, NOT in CORDIS. Searching the wrong database produced a false fabrication flag. The skill must check national grant databases beyond CORDIS whenever a country's national programmes use EU structural funds.
These three failures are different shapes of the same gap: deep multi-method entity verification. This skill closes that gap.
Use deep-verify whenever the proposal makes a non-trivial claim about an external entity that needs to be defensible to an evaluator. The entity can be of ANY of the following types — this skill is general-purpose, not partner-specific:
| Entity type | Example proposal claim | What deep-verify will check |
|---|---|---|
| organisation (company, NGO, university, lab) | "NOETIV has solutions deployed in real clinical settings" | Existence in national registry, active website (with SPA inspection), team credentials, products, grants history |
| person (researcher, PI, advisor, founder) | "Prof. Vassilios Verykios, 170+ papers, 10,100+ citations, founder of the Big Data Analytics & Anonymization Lab" | ORCID, Google Scholar, Semantic Scholar, dblp, institutional affiliation, h-index, publication count |
| project (cited prior work, competitor) | "NOUS (CORDIS 101135927) established the European cloud-edge-HPC continuum" | CORDIS + national grant DBs, deliverables produced, TRL achieved, project website status |
| product / technology | "Eclipse Aidge from CEA-List, open-source embedded AI compiler under Eclipse Foundation" | Repository existence, licence, maintainer, recent activity, deployment evidence |
| standard / specification | "We adopt ISO/IEC 5259-3 — first certification issued globally November 2025" | Standards body verification, publication date, current revision |
| certification / accreditation | "Almawave is ISO/IEC 42001 certified by DNV (June 2025)" | Certification body's public registry, issuance date, scope |
| regulatory pathway | "MDR Class IIa under Annex VIII Rule 11" | Cite the actual rule text, verify the classification logic |
| clinical trial / dataset | "CAPTURE-HF — 1,600 patients, 20 sites, AUC 0.821 PCWP" | ClinicalTrials.gov, EudraCT, conference proceedings (HFSA, ESC), dataset publication |
| working group membership | "SCYTALES represents Sweden in ISO/IEC JTC1/SC17/WG10" | Standards body's public member list |
| consortium partner (the canonical default) | "Arc Sentinel SRL leads WP10 (Children Online Safety pilot)" | All of the above combined: registry + website + team + products + grants + GitHub + WP-role defensibility |
Trigger heuristics — when to invoke deep-verify automatically:
/gw:fact-check and you want to re-verify with deeper methodsWhen NOT to use this skill:
/gw:fact-check (CrossRef + Semantic Scholar are correct here)/gw:landscape (OpenAIRE topic search is the right tool)/gw:import-docs/gw:fact-checkdeep-verify is the right tool when you need to go deeper than these other skills — when basic search has failed or is structurally unable to find the evidence.
Other skills in the gw plugin can call deep-verify ad-hoc for any cited entity using the --inline-entity flag. The pattern is the same in every case: pass an entity record as JSON and read back a verification report.
# fact-check finds an entity-mention claim it can't verify via CrossRef
uv run gw-deepverify --inline-entity '{
"id": "almawave",
"name": "Almawave",
"type": "organisation",
"country": "IT",
"claims": ["ISO/IEC 42001 certified by DNV June 2025"]
}'
# landscape verifies a competitor project
uv run gw-deepverify --inline-entity '{
"id": "nous-101135927",
"name": "NOUS",
"type": "project",
"claims": ["delivered cloud-edge-HPC continuum at TRL 7"]
}'
# literature verifies an author affiliation
uv run gw-deepverify --inline-entity '{
"id": "verykios",
"name": "Vassilios Verykios",
"type": "person",
"claims": ["Full Professor at Hellenic Open University, 170+ papers"]
}'
# proposal verifies a cited technology
uv run gw-deepverify --inline-entity '{
"id": "eclipse-aidge",
"name": "Eclipse Aidge",
"type": "product",
"claims": ["open-source embedded AI compiler from CEA-List"]
}'
Each invocation returns a JSON verification report with the same schema: evidence trail, tier, defensibility, sources.
--proposal-dir <path> — Required for pipeline-phase invocation. Proposal directory.--entities <path> — Optional. Path to YAML/JSON list of entities to verify. If absent, defaults to consortium-partner workflow (auto-load partners.yaml).--entity-id <id> — Optional. Verify a single entity (re-runs).--inline-entity '<json>' — Optional. Verify one entity from inline JSON, no file needed. Used when called from another skill.--no-multi-model — Force single-Claude tier assignment instead of mm-ask multi-model consensus.--no-spa — Disable SPA bundle inspection (speed).--no-wayback — Disable Wayback Machine fallback.--depth <quick|standard|deep> — Default: standard.--output-format <md|json|both> — Default: both.Locate plugin root (required before any tool invocations) using the standard pattern:
if [ -f "tools/verify_setup.py" ]; then
GW_ROOT="$(pwd)"
else
GW_ROOT=$(find ".claude/plugins" "$HOME/.claude/plugins" -maxdepth 8 -name "verify_setup.py" -path "*gw*" 2>/dev/null | head -1 | xargs dirname | xargs dirname)
fi
export GW_ROOT
Multi-model detection — same idiom as /gw:fact-check:
MM_AVAILABLE=0
if uv run mm-detect --json 2>/dev/null | python3 -c "
import json, sys d = json.load(sys.stdin) sys.exit(0 if any(v.get('installed') for v in d.get('providers', {}).values()) else 1) "; then MM_AVAILABLE=1 fi
If `--no-multi-model` is set, force `MM_AVAILABLE=0`.
### Phase 1: Load entities
Precedence (try each in order):
1. `--inline-entity '<json>'` — verify only that one entity, skip the file lookup
2. `--entities <path>` — explicit file
3. `<proposal_dir>/deep_verify/entities.yaml` — canonical location (preferred)
4. `<proposal_dir>/deep_verify/partners.yaml` — legacy partner schema (auto-mapped)
5. `<proposal_dir>/supporting/consortium_partners.yaml` — emitted by `/gw:import-docs`
```bash
uv run gw-deepverify load-entities "$proposal_dir"
If none of the above succeed, prompt the PI to provide a list.
For each entity in the list, create a working subdirectory <proposal_dir>/deep_verify/<entity_id>/ and run all the tracks applicable to the entity's type (per data/entity_type_track_map.yaml).
For each URL in the entity's urls list:
uv run gw-deepverify fetch-url "<url>" --spa > "$dv_dir/$entity_id/fetch_$(echo $url | tr / _).json"
The output JSON includes is_spa, spa_framework, spa_bundles, body_text, title. If is_spa: true AND the body text is sparse, immediately download and extract each bundle:
for bundle in $(jq -r '.spa_bundles[]' "$dv_dir/$entity_id/fetch.json"); do
uv run gw-deepverify spa-extract "$bundle" \
--max-mb 5 > "$dv_dir/$entity_id/spa_extract.json"
done
The spa-extract output contains URLs, emails, subdomains, paths, proper nouns, ISO dates, and currency amounts found inside the bundle. This is the difference between concluding "placeholder website" and finding the real product, team, and pricing.
uv run gw-deepverify subdomain-sweep "<domain>" > "$dv_dir/$entity_id/subdomains.json"
Pay special attention to product-name subdomains discovered in the SPA bundle (e.g., the NOETIV bundle revealed skin.noetiv.com and auth.noetiv.com — both live).
uv run gw-deepverify probe-subpages "<base_url>" > "$dv_dir/$entity_id/subpages.json"
Returns 200/404/403 per path. A site with /about, /team, /products all returning 200 is real even if the homepage looks empty in WebFetch.
Trigger if any of:
--depth deep is setuv run gw-deepverify wayback "<url>" --limit 10 > "$dv_dir/$entity_id/wayback.json"
The Wayback Machine often renders SPA snapshots as static HTML, providing a path through the SPA-blind problem.
For each contact in the entity's contacts list whose name contains non-Latin characters OR is known to be a romanisation of a non-Latin name:
uv run gw-deepverify transliterate "<name>" --script greek > "$dv_dir/$entity_id/transliteration_$contact.json"
Use the resulting variants when running Track I (academic credentials) and when searching LinkedIn / personal websites. Critical: this is what catches Yiorgos Bakalis ↔ George Bakellas.
uv run gw-deepverify registry-lookup "<country_code>" "<legal_name>" > "$dv_dir/$entity_id/registry.json"
Uses data/registries.yaml. v0.1 supports RO, GR, IT, DE, UK. Other countries fall back to OpenCorporates with a "registry coverage missing" warning.
If the entity has a known GitHub org (provided in the entity record, or discovered via WebSearch for "<entity name> github"):
uv run gw-deepverify github-org "<org_name>" --max-repos 100 > "$dv_dir/$entity_id/github.json"
For each named contact, also search GitHub users:
uv run gw-deepverify github-user-search "<contact name or transliteration variant>" \
> "$dv_dir/$entity_id/github_user_$contact.json"
Cross-entity link detection: when iterating multiple entities, compare the GitHub commit authors. If the first commit author of one entity's repo matches a known person in another entity's team, log the cross-link in the synthesis report. This is how the Damian↔Damian SmartClover↔Ratio1 connection was found.
If GITHUB_TOKEN is set in the environment, the rate limit goes from 60 to 5,000 requests/hour.
uv run gw-funded pi-grants "<entity name>" --agency horizon \
> "$dv_dir/$entity_id/grants_cordis.json"
uv run gw-deepverify national-grants "<country>" "<entity name>" \
> "$dv_dir/$entity_id/grants_national.json"
Critical: if CORDIS returns empty but the entity is in a country with national operational programmes using ERDF/ESF+ structural funds (Romania, Italy, France, Germany, Spain, Poland, etc.), Track H MUST also query the national database before concluding "no grants found". The AI STM POCIDIF case is the canonical failure mode.
person type or organisation contacts)uv run gw-deepverify orcid-search "<person name>" \
--affiliation "<institution>" > "$dv_dir/$entity_id/orcid_$person.json"
Try each transliteration variant from Track E. Cross-reference with Google Scholar / Semantic Scholar / dblp via WebSearch.
product type)Search Google Play, Apple App Store, Microsoft Store, GitHub releases, package registries (PyPI for Python, npm for JavaScript, crates.io for Rust). Document download counts where visible.
uv run gw-deepverify whois-dns "<domain>" > "$dv_dir/$entity_id/whois.json"
Compare the domain's creation_date against the entity's claimed founding year. A claim of "10 years operational" with a domain registered last month is a credible-evidence flag.
For working_group entities: fetch the standards body's public member list (CEN/CENELEC/ETSI/IEEE/W3C/IETF) and verify the entity is listed.
For standard entities: fetch the ISO catalogue or the relevant body's catalogue and verify the standard's publication date and current revision.
For regulatory entities: fetch the EUR-Lex text of the regulation/directive/annex and verify the cited article/rule.
For clinical_trial entities: query ClinicalTrials.gov and EudraCT.
If MM_AVAILABLE=1, after the deterministic tracks complete, pass a summary
of the evidence to mm-ask for enrichment. Dispatch to 3 external models
in parallel so the enrichment itself gets cross-model consensus:
cat > /tmp/gw_dv_enrich_${entity_id}.txt <<EOF
For entity "$entity_name" ($country, $type), confirm existence and the
following claims: $claims
Use the evidence already gathered (pasted below) as the baseline. Return
JSON with the schema:
{
"existence": "confirmed" | "uncertain" | "refuted",
"registry_match": "exact" | "partial" | "none",
"grants": [...],
"products": [...],
"team_members": [...],
"concerns": [...]
}
Return ONLY the JSON. No prose. No markdown fence.
--- EVIDENCE PACK ---
$(cat "$proposal_dir/deep_verify/${entity_id}/evidence.json")
EOF
uv run mm-ask \
--models gpt-5.4-high,gemini-3.1-pro,grok-4-20-thinking \
--prompt-file "/tmp/gw_dv_enrich_${entity_id}.txt" \
--output "$proposal_dir/deep_verify/${entity_id}/mm_enrichment.json" \
--timeout 300
Then, for tier assignment, run a second mm-ask call with a devil's-advocate
prompt — the three reviewers argue the opposite tier and the skill declares
a tier only if ≥2 of 3 agree:
cat > /tmp/gw_dv_tier_${entity_id}.txt <<EOF
Given the evidence pack below for entity "$entity_name", assign a
credibility tier from T1 (fully verified) to T5 (fabricated or
unverifiable) per the rubric in data/credibility_rubric.md.
IMPORTANT: Adopt a devil's advocate perspective. Assume the tier OPPOSITE
your first instinct is correct, and actively try to refute the tier you
would have assigned. Only after that refutation pass, commit to a final
tier.
Return a JSON object:
{"tier": "T1|T2|T3|T4|T5",
"rationale": "why this tier despite the devil's advocate pass",
"confidence": 1-5}
--- EVIDENCE PACK ---
$(cat "$proposal_dir/deep_verify/${entity_id}/evidence.json")
--- RUBRIC ---
$(cat data/credibility_rubric.md)
EOF
uv run mm-ask \
--models gpt-5.4-high,gemini-3.1-pro,grok-4-20-thinking \
--prompt-file "/tmp/gw_dv_tier_${entity_id}.txt" \
--output "$proposal_dir/deep_verify/${entity_id}/mm_tier.json" \
--timeout 300
Merge the 3 reviewers' tier assignments. If ≥2 agree, that's the final tier
with synthesized_by: "mm_ask_3model_consensus". If all three disagree,
flag as CONTESTED and default to the more pessimistic tier (e.g., if T2 / T3 / T4
split, assign T4 and note the disagreement).
This is the one place multi-model consensus is load-bearing — tier assignment is judgement against a rubric, and single-model confirmation bias is the exact failure mode that produced the v1 NOETIV "near-fatal" verdict. Three independent models with a devil's-advocate prompt is the antidote.
Without multi-model: run a single Claude pass against the rubric, with an
explicit "devil's advocate" prompt that forces a refutation pass before
settling on a tier. Log synthesized_by: "claude_alone" and warn the user.
Render <proposal_dir>/deep_verify/<entity_id>_verification.md from data/entity_report_template.md. Also emit <proposal_dir>/deep_verify/<entity_id>/verification.json with the structured evidence.
The report ends with a "Verified description (paste-ready)" block grounded strictly in verified facts. For consortium partners, this targets Section 3.2; for cited projects, the State of the Art; for products, the methodology; etc.
uv run gw-deepverify synthesize "$proposal_dir" \
> "$proposal_dir/deep_verify/synthesis.json"
Render the Markdown synthesis from data/synthesis_template.md with the credibility risk matrix, tier counts, blocking issues, and cross-entity links.
fail_on_critical: true AND that entity holds a load-bearing role: WP-lead in a consortium, foundational citation, etc.)If BLOCK, halt the pipeline and surface the issues to the PI.
Present the synthesis table to the PI. For each T4/T5 entity, show the evidence trail and the recommended action (PI provides primary documentation, restructures the entity's role, or removes the entity from the proposal).
Accept PI overrides but record them with justification in synthesis.json (pi_override: true, override_reason: "...", override_timestamp: "...").
uv run gw-state update "$proposal_dir" --phase deep_verification --status complete
(Or in_progress if blocked at the gate.)
| Tier | Label | Criteria | Action |
|---|---|---|---|
| T1 | verified_strong | Multiple authoritative sources confirm entity AND all key claims | PROCEED. Use the verified description verbatim. |
| T2 | verified_moderate | Entity confirmed by ≥1 authoritative source. Most claims supported, minor gaps. | PROCEED. Tighten the proposal text to match what was verified. |
| T3 | low_moderate | Entity exists but claims partially supported. Thin operational footprint. | WARN. Soften the proposal language. |
| T4 | unverified_risky | Cannot confirm key claims; OR all tracks returned empty (default for evidence-gathering failure). | WARN. NOT a fabrication flag. PI must provide primary documentation. |
| T5 | fabricated | Active contradiction between claims and evidence; OR entity does not exist under stated name/country/identifier. | BLOCK. Correct the claim or remove the entity. |
Critical: T5 is reserved for ACTIVE CONTRADICTION. When all tracks fail to find evidence, the default is T4, not T5. Empty evidence is a "we need more information" flag, not a "this is fake" flag. The v1 NOETIV failure that triggered this skill assigned the wrong tier because the v1 agent treated "no evidence found" as "fabricated".
Full rubric with worked examples: data/credibility_rubric.md.
| Failure | Detection | Graceful degradation |
|---|---|---|
Country not in registries.yaml | Missing key | Fall back to OpenCorporates + WebSearch; warn once; log registry_coverage: missing |
SPA bundle exceeds spa_max_bundle_mb | File size check | Heuristic regex over the truncated bundle for entity-name + descriptor patterns; mark bundle_inspection: partial |
| SPA bundle is heavily minified/obfuscated | Identifier entropy heuristic | Fall back to Wayback Machine; if still empty, flag as "website unreadable"; do NOT rate T5 on this alone |
| Wayback Machine has no snapshot | CDX returns empty | Log wayback_snapshots: 0; continue; do NOT penalise (new domains legitimately have no snapshots) |
| GitHub API rate-limited (no token) | HTTP 403 | Back off with exponential retry; if still failing, skip GitHub track, warn once; do NOT downgrade |
| WHOIS blocked / returns empty | Empty output | Log whois: unavailable; continue; do not penalise |
| Entity name ambiguous (multiple legal entities) | Registry returns >1 match | Halt and ask PI to disambiguate by registration number |
External API timeout during mm-ask dispatch | Exception | mm-ask surfaces per-model failures; continue with successful workers. If all three fail, fall back to single-Claude synthesis for that entity and annotate verified_by: "claude_alone" |
| All tracks fail for one entity | No successful track | Assign T4, NOT T5. Emit a clear "evidence could not be gathered" warning. Recommend PI contact entity for primary documentation. |
partners.yaml missing and PI has no list | Interactive prompt | Require the PI to paste partners; do not silently skip the phase |
| PI overrides a tier | Interactive | Accept; record pi_override: true + reason in synthesis.json |
The skill reads partner_verification: (legacy name accepted) or deep_verify: from config.yaml:
deep_verify:
enabled: true
depth: standard # quick | standard | deep
spa_inspection: true
spa_max_bundle_mb: 5
wayback_fallback: true
wayback_max_snapshots: 10
transliteration_variants: true
github_deep_enum: true
github_max_repos_per_org: 100
national_registries: true
national_grants: true
whois_dns: true
fail_on_critical: true
min_credibility_tier: low_moderate # T3
rate_limit_delay_ms: 500
parallel_entities: 1
default_workflow: consortium_partners
After a successful run:
<proposal_dir>/deep_verify/
├── entities.yaml # canonical entity list (created if absent)
├── synthesis.md # human-readable risk matrix
├── synthesis.json # structured aggregated output
├── <entity_id>_verification.md # one per entity, paste-ready
└── <entity_id>/
├── verification.json # structured per-entity evidence
├── fetch_*.json # per-URL fetch results
├── spa_extract.json # JS bundle extraction findings
├── subpages.json # subpage probe results
├── subdomains.json # subdomain sweep results
├── wayback.json # Wayback Machine snapshots
├── registry.json # national registry lookup
├── grants_cordis.json # CORDIS lookup
├── grants_national.json # national grant DB lookup
├── github.json # GitHub org enumeration
├── github_user_*.json # per-contact GitHub user search
├── orcid_*.json # per-contact ORCID search
├── whois.json # WHOIS + DNS
└── transliteration_*.json # per-contact name variants
When other gw skills consume the output, they should read synthesis.json for the aggregated tier matrix and the per-entity verification.json for full evidence. The paste-ready descriptions are inside the per-entity Markdown files at the "Verified description (paste-ready)" section.
/gw:supporting-docs — fills consortium agreement partner descriptions from corrected_description fields/gw:proposal — Section 3.2 (Consortium) pulls partner descriptions; State of the Art pulls verified project descriptions/gw:fact-check — pre-populates entity-claim ratings (T1-T2 = pre-VERIFIED, T5 = pre-FABRICATED)/gw:landscape — verifies top-N competing projects from OpenAIRE results/gw:literature — verifies cited author affiliations/gw:review — Skeptic persona cross-checks proposal text against synthesis.json for exaggeration/gw:risk-analysis — surfaces T4 entities as execution risksProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub stmailabs/gw --plugin gw