From 82ae5b237f4251513b9115ffaaaadb09c33df743 Mon Sep 17 00:00:00 2001 From: Albert Najjar Date: Mon, 31 Aug 2026 08:20:59 -0400 Subject: [PATCH 01/13] add packet generalization boundary gate Land the anti-contamination checker, docs, CI wiring, and failing black-box agent tests before planner deletion on the decontamination lane. Co-authored-by: Cursor --- .github/workflows/retrieval-engine-smoke.yml | 14 + .github/workflows/source-proof.yml | 6 + .../tests/packet_generalization_boundary.rs | 201 +++++++++++++ docs/architecture/overview.md | 1 + docs/architecture/packet-generalization.md | 48 +++ .../check-packet-generalization-boundary.mjs | 13 + .../lib/packet-generalization-boundary.mjs | 283 ++++++++++++++++++ ...ck-packet-generalization-boundary.test.mjs | 158 ++++++++++ 8 files changed, 724 insertions(+) create mode 100644 crates/codestory-agent/tests/packet_generalization_boundary.rs create mode 100644 docs/architecture/packet-generalization.md create mode 100644 scripts/check-packet-generalization-boundary.mjs create mode 100644 scripts/lib/packet-generalization-boundary.mjs create mode 100644 scripts/tests/check-packet-generalization-boundary.test.mjs diff --git a/.github/workflows/retrieval-engine-smoke.yml b/.github/workflows/retrieval-engine-smoke.yml index 25acbe250..249e74d88 100644 --- a/.github/workflows/retrieval-engine-smoke.yml +++ b/.github/workflows/retrieval-engine-smoke.yml @@ -43,10 +43,14 @@ on: - scripts/lint-retrieval-generalization.mjs - scripts/lib/retrieval-generalization-lint.mjs - scripts/tests/lint-retrieval-generalization.test.mjs + - scripts/check-packet-generalization-boundary.mjs + - scripts/lib/packet-generalization-boundary.mjs + - scripts/tests/check-packet-generalization-boundary.test.mjs - scripts/prepare-embedded-model.mjs - docs/contributors/testing-matrix.md - docs/ops/retrieval-engine.md - docs/testing/retrieval-architecture.md + - docs/architecture/packet-generalization.md # Base-branch runs seed caches that sibling PRs are allowed to restore. push: branches: @@ -88,10 +92,14 @@ on: - scripts/lint-retrieval-generalization.mjs - scripts/lib/retrieval-generalization-lint.mjs - scripts/tests/lint-retrieval-generalization.test.mjs + - scripts/check-packet-generalization-boundary.mjs + - scripts/lib/packet-generalization-boundary.mjs + - scripts/tests/check-packet-generalization-boundary.test.mjs - scripts/prepare-embedded-model.mjs - docs/contributors/testing-matrix.md - docs/ops/retrieval-engine.md - docs/testing/retrieval-architecture.md + - docs/architecture/packet-generalization.md workflow_dispatch: permissions: @@ -118,9 +126,15 @@ jobs: - name: Generalization lint (production paths) run: node scripts/lint-retrieval-generalization.mjs + - name: Packet generalization boundary + run: node scripts/check-packet-generalization-boundary.mjs + - name: Generalization lint hostile matrix run: node --test scripts/tests/lint-retrieval-generalization.test.mjs + - name: Packet generalization boundary hostile matrix + run: node --test scripts/tests/check-packet-generalization-boundary.test.mjs + - name: Release evidence gate contracts run: node --test scripts/tests/codestory-release-evidence-gate.test.mjs diff --git a/.github/workflows/source-proof.yml b/.github/workflows/source-proof.yml index 71b4479cd..df968e294 100644 --- a/.github/workflows/source-proof.yml +++ b/.github/workflows/source-proof.yml @@ -918,9 +918,15 @@ jobs: - name: Generalization lint (production paths) run: node scripts/lint-retrieval-generalization.mjs + - name: Packet generalization boundary + run: node scripts/check-packet-generalization-boundary.mjs + - name: Generalization lint hostile matrix run: node --test scripts/tests/lint-retrieval-generalization.test.mjs + - name: Packet generalization boundary hostile matrix + run: node --test scripts/tests/check-packet-generalization-boundary.test.mjs + windows-native-contracts: name: windows-native-contracts needs: resolve diff --git a/crates/codestory-agent/tests/packet_generalization_boundary.rs b/crates/codestory-agent/tests/packet_generalization_boundary.rs new file mode 100644 index 000000000..3eeb94fee --- /dev/null +++ b/crates/codestory-agent/tests/packet_generalization_boundary.rs @@ -0,0 +1,201 @@ +//! Black-box packet generalization boundary tests (Phase 2→3). +//! +//! These encode the decontaminated planner contract: domain nouns alone must +//! not create fixed obligations, prompt words must not delete evidence, encoded +//! brand bytes must behave like ordinary tokens, and bijective renames must +//! preserve repository-derived objectives. +//! +//! On the contaminated head these tests fail. Phase 3 makes them pass by +//! deleting taxonomy/cleanup surfaces and landing `repository_evidence_plan`. + +use codestory_agent::packet_obligations::build_packet_obligation_plan; +use codestory_agent::packet_plan::build_packet_plan_with_extra; +use codestory_agent::packet_scoring::{ + packet_drop_unrequested_markdown_siblings, packet_terms_contain, +}; +use codestory_agent::packet_terms::{ + packet_probe_terms, packet_terms_indicate_client_send_flow, + packet_terms_indicate_hook_cache_flow, packet_terms_indicate_mapper_configuration_plan_flow, + packet_terms_indicate_prepared_session_adapter_flow, +}; +use codestory_contracts::api::{ + AgentCitationDto, NodeId, NodeKind, PacketBudgetModeDto, SearchHitOrigin, +}; + +fn domain_noun_prompt(noun: &str) -> String { + format!("Explain how the {noun} works in this repository.") +} + +#[test] +fn domain_nouns_do_not_create_fixed_flow_obligations() { + let nouns = [ + "client", + "cache", + "formatter", + "mapper", + "request", + "animation", + ]; + for noun in nouns { + let question = domain_noun_prompt(noun); + let terms = packet_probe_terms(&question); + assert!( + !packet_terms_indicate_client_send_flow(&terms) + && !packet_terms_indicate_hook_cache_flow(&terms) + && !packet_terms_indicate_mapper_configuration_plan_flow(&terms), + "domain noun `{noun}` activated a production flow classifier" + ); + let plan = build_packet_plan_with_extra( + &question, + None, + PacketBudgetModeDto::Standard, + &[], + ); + let obligations = build_packet_obligation_plan(&question, plan.task_class, &plan.queries); + // Contaminated planners mint fixed stage obligations from vocabulary + // via flow_requirements. Generic profile guards may remain until + // Phase 3 replaces sufficiency with repository-derived objectives. + let taxonomy_shaped = obligations.claim_obligations.iter().any(|obligation| { + let id = obligation.id.to_ascii_lowercase(); + id.contains("client_transport") + || id.contains("hook_cache") + || id.contains("mapper_configuration") + || id.contains("stylesheet_animation") + || id.contains("runtime_formatting") + || id.contains("prepared_session") + || id.contains("request_dispatch") + }); + assert!( + !taxonomy_shaped, + "domain noun `{noun}` created taxonomy-shaped obligations: {:?}", + obligations + .claim_obligations + .iter() + .map(|o| o.id.as_str()) + .collect::>() + ); + } + + // Stronger contamination probe: noun + typical holdout verbs must still + // not select a fixed domain stage list after decontamination. + let contaminated_prompt = + "Explain how the client request session adapter send path works with cache hooks"; + let contaminated_terms = packet_probe_terms(contaminated_prompt); + assert!( + !packet_terms_indicate_client_send_flow(&contaminated_terms) + && !packet_terms_indicate_prepared_session_adapter_flow(&contaminated_terms) + && !packet_terms_indicate_hook_cache_flow(&contaminated_terms), + "domain vocabulary still selects fixed flow classifiers" + ); +} + +#[test] +fn prompt_words_cannot_delete_retrieved_evidence() { + let markdown = AgentCitationDto { + node_id: NodeId("n-md".into()), + display_name: "README.md".into(), + kind: NodeKind::FILE, + file_path: Some("README.md".into()), + line: Some(1), + score: 1.0, + origin: SearchHitOrigin::TextMatch, + target: None, + resolvable: true, + subgraph_id: None, + evidence_edge_ids: Vec::new(), + retrieval_score_breakdown: None, + evidence_tier: None, + evidence_producer: None, + resolution_status: None, + loss_reason: None, + coverage_role: None, + eligible_for_sufficiency: None, + source_excerpt: Some("# readme".into()), + }; + let code = AgentCitationDto { + node_id: NodeId("n-code".into()), + display_name: "format".into(), + kind: NodeKind::METHOD, + file_path: Some("src/format.rs".into()), + line: Some(10), + score: 1.0, + origin: SearchHitOrigin::IndexedSymbol, + target: None, + resolvable: true, + subgraph_id: None, + evidence_edge_ids: Vec::new(), + retrieval_score_breakdown: None, + evidence_tier: None, + evidence_producer: None, + resolution_status: None, + loss_reason: None, + coverage_role: None, + eligible_for_sufficiency: None, + source_excerpt: Some("fn format() {}".into()), + }; + // Contaminated cleanup deletes markdown siblings when formatting-flow + // vocabulary appears in the prompt, even though the user never asked to + // drop README evidence. + let question = "Explain the runtime formatting output path"; + let terms = packet_probe_terms(question); + let mut rows = vec![markdown, code]; + let before = rows.len(); + packet_drop_unrequested_markdown_siblings(&mut rows, &terms); + assert_eq!( + rows.len(), + before, + "prompt vocabulary alone deleted retrieved evidence" + ); +} + +#[test] +fn encoded_brand_bytes_do_not_activate_domain_flow_classifiers() { + // Contaminated code treats [115,119,114] / "swr" as a hook-cache flow. + // Decontaminated code must treat it like any other token. + let encoded_terms = packet_probe_terms("how does swr cache requests?"); + let ordinary_terms = packet_probe_terms("how does xyz cache requests?"); + assert_eq!( + packet_terms_indicate_hook_cache_flow(&encoded_terms), + packet_terms_indicate_hook_cache_flow(&ordinary_terms), + "encoded brand token activated a domain classifier differently from an ordinary token" + ); + // Once classifiers are deleted, both sides are false; until then this + // pins the equivalence requirement even if both incorrectly return true. + let _ = ( + packet_terms_indicate_client_send_flow(&encoded_terms), + packet_terms_indicate_mapper_configuration_plan_flow(&ordinary_terms), + packet_terms_contain(&encoded_terms, "swr"), + ); +} + +#[test] +fn bijective_rename_preserves_seed_query_objectives() { + // Until repository_evidence_plan lands, seed plans must at least treat + // renamed explicit anchors as first-class seeds rather than domain stages. + let original = "Trace Foo::bar in src/foo.rs calling Baz::qux"; + let renamed = "Trace Alpha::beta in src/alpha.rs calling Gamma::delta"; + let original_plan = + build_packet_plan_with_extra(original, None, PacketBudgetModeDto::Standard, &[]); + let renamed_plan = + build_packet_plan_with_extra(renamed, None, PacketBudgetModeDto::Standard, &[]); + let original_has_path = original_plan + .queries + .iter() + .any(|q| q.query.contains("src/foo.rs")); + let renamed_has_path = renamed_plan + .queries + .iter() + .any(|q| q.query.contains("src/alpha.rs")); + assert!(original_has_path && renamed_has_path); + // Domain stage seeds must not appear for either spelling. + for plan in [&original_plan, &renamed_plan] { + assert!( + !plan.queries.iter().any(|q| { + let purpose = q.purpose.to_ascii_lowercase(); + purpose.contains("flow-role") || purpose.contains("flow role") + }), + "seed plan still expands flow-role taxonomy queries: {:?}", + plan.queries + ); + } +} diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 1b5b71dc5..202b79187 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -160,5 +160,6 @@ for measurement. - Per-request orchestration: [runtime-execution-path.md](runtime-execution-path.md) - Core indexing: [indexing-pipeline.md](indexing-pipeline.md) - Retrieval publication and readiness: [retrieval-design.md](retrieval-design.md) +- Packet generalization boundary: [packet-generalization.md](packet-generalization.md) - Language claim tiers: [language-support.md](language-support.md) - Crate ownership: [subsystems/](subsystems/) diff --git a/docs/architecture/packet-generalization.md b/docs/architecture/packet-generalization.md new file mode 100644 index 000000000..4df048036 --- /dev/null +++ b/docs/architecture/packet-generalization.md @@ -0,0 +1,48 @@ +# Packet generalization + +Packet planning must stay repository-evidence driven. Prompt text may supply +generic seeds (paths, qualified symbols, identifiers, original wording, and +explicit probes). It must not select domain taxonomies, fixed flow stage lists, +carrier predicates, post-rank cleanup passes, or flow-template claims. + +## Required sequence + +```text +generic seed plan → uncapped seed retrieval → bounded pinned graph + → repository evidence plan → hydrate → identity/range dedup → 16-row/16 KiB projection +``` + +One pinned publication. One bounded retry when that publication changes. The +public packet budget remains 16 evidence rows / 16 KiB. + +## Forbidden production shapes + +- Prompt → domain-flow classifiers (including ASCII byte-array encodings of + brand or holdout terms such as `[115, 119, 114]` → `swr`) +- Fixed stage lists and flow-requirement dispatchers keyed by those classifiers +- Domain evidence-carrier predicates and post-rank sibling cleanup passes +- Flow-template claims and holdout `expected_files` / `expected_symbols` anchors +- Production dependencies on `benchmarks/`, `codestory-bench`, or eval manifests + +Historical 18-task / language-expansion holdout scores are +`evidence_eligibility: contaminated_development` only. They are never a release +or generalization gate. + +## Boundary checker + +CI job `retrieval-generalization` runs: + +- `node scripts/lint-retrieval-generalization.mjs` +- `node scripts/check-packet-generalization-boundary.mjs` +- their hostile Node test suites + +The packet boundary checker fails closed on contaminated heads. Renaming a +classifier while keeping encoded brands, holdout anchors, or deleted cleanup +APIs must still fail. Vocabulary for those banned shapes is permitted only in +tests, tooling, and the checker fixtures. + +## Claims + +Packet claims may state only observed structural facts with node or edge +identity, plus dynamic gaps / continuation / unknown. They must not infer +absence or runtime-execution truth from a missing row. diff --git a/scripts/check-packet-generalization-boundary.mjs b/scripts/check-packet-generalization-boundary.mjs new file mode 100644 index 000000000..9c416f373 --- /dev/null +++ b/scripts/check-packet-generalization-boundary.mjs @@ -0,0 +1,13 @@ +#!/usr/bin/env node +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { runPacketGeneralizationBoundaryCheck } from "./lib/packet-generalization-boundary.mjs"; + +const repoRoot = process.argv[2] + ? path.resolve(process.argv[2]) + : path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const result = runPacketGeneralizationBoundaryCheck(repoRoot); +process.stdout.write(result.stdout); +process.stderr.write(result.stderr); +process.exitCode = result.exitCode; diff --git a/scripts/lib/packet-generalization-boundary.mjs b/scripts/lib/packet-generalization-boundary.mjs new file mode 100644 index 000000000..35da19bb2 --- /dev/null +++ b/scripts/lib/packet-generalization-boundary.mjs @@ -0,0 +1,283 @@ +/** + * Packet generalization boundary: keep production packet planning free of + * benchmark corpora, holdout expected anchors, encoded brand detectors, and + * deleted domain taxonomy / cleanup APIs. + * + * Vocabulary for those banned shapes is permitted only in tests, tooling, and + * this checker / its fixtures. + */ +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +export const DELETED_TAXONOMY_APIS = Object.freeze([ + "packet_flow_requirements_for_terms", + "append_flow_template_claims", + "packet_append_event_output_flow_template_claims", + "packet_append_indexing_pipeline_flow_template_claims", + "packet_terms_indicate_indexing_flow", + "packet_terms_indicate_request_dispatch_flow", + "packet_terms_indicate_server_request_dispatch_flow", + "packet_terms_indicate_server_route_dispatch_flow", + "packet_terms_indicate_javascript_route_source_flow", + "packet_terms_indicate_route_tree_dispatch_flow", + "packet_terms_indicate_buffered_io_flow", + "packet_terms_indicate_site_build_phase_flow", + "packet_terms_indicate_log_record_handler_flow", + "packet_terms_indicate_mapper_configuration_plan_flow", + "packet_terms_indicate_prepared_session_adapter_flow", + "packet_terms_indicate_search_execution_flow", + "packet_terms_indicate_stylesheet_animation_flow", + "packet_terms_indicate_html_css_template_structure_flow", + "packet_terms_indicate_sql_schema_flow", + "packet_terms_indicate_hook_cache_flow", + "packet_terms_indicate_client_send_flow", + "packet_terms_indicate_full_outbound_request_flow", + "packet_terms_indicate_form_validation_flow", + "packet_terms_indicate_event_loop_command_flow", + "packet_terms_indicate_command_server_bootstrap_flow", + "packet_terms_indicate_command_event_loop_flow", + "packet_terms_indicate_network_command_input_flow", + "packet_terms_indicate_command_dispatch_flow", + "packet_terms_indicate_url_session_request_flow", + "packet_terms_indicate_shell_version_use_flow", + "packet_terms_indicate_shell_install_dispatch_flow", + "packet_terms_indicate_string_predicate_flow", + "packet_terms_indicate_runtime_formatting_flow", + "packet_drop_unrequested_wide_char_siblings", + "packet_drop_unrequested_python_siblings", + "packet_drop_unrequested_windows_formatting_siblings", + "packet_drop_unrequested_formatting_extension_siblings", + "packet_drop_unrequested_formatter_specialization_siblings", + "packet_drop_unrequested_single_letter_displays", + "packet_drop_unrequested_named_client_adapter_siblings", + "packet_drop_unrequested_example_and_binding_siblings", + "packet_drop_unrequested_mapper_annotation_siblings", + "packet_drop_unrequested_test_siblings", + "packet_keep_shared_source_set_over_platform_duplicates", + "packet_drop_unrequested_sql_schema_variant_siblings", + "packet_drop_excess_unrequested_keyframe_siblings", + "packet_drop_excess_unrequested_animation_class_siblings", + "packet_drop_unrequested_animation_file_aliases", + "packet_drop_unrequested_animation_file_only_sheets", + "packet_drop_unrequested_non_stylesheet_animation_siblings", + "packet_drop_unrequested_repo_root_stylesheet_siblings", + "packet_drop_unrequested_non_primary_flow_siblings", + "packet_drop_unrequested_duplicate_client_type_paths", + "packet_drop_unrequested_export_macro_displays", + "packet_drop_unrequested_system_format_failure_siblings", + "packet_drop_unrequested_markdown_siblings", +]); + +/** Historical holdout anchors that must never steer production packet code. */ +export const HISTORICAL_EXPECTED_ANCHORS = Object.freeze([ + "src/requests/api.py", + "src/requests/sessions.py", + "src/requests/models.py", + "src/requests/adapters.py", + "PreparedRequest.prepare", + "HTTPAdapter.send", + "dart-http-client-flow", + "language-expansion-holdout", + "typescript-swr-hook-flow", + "vercel-swr", + "dart-lang-http", +]); + +const PRODUCTION_SCAN_GLOBS = Object.freeze([ + "crates/codestory-agent/src", + "crates/codestory-runtime/src/agent", +]); + +const PERMITTED_VOCABULARY_PATH_FRAGMENTS = Object.freeze([ + `${path.sep}tests${path.sep}`, + `${path.sep}scripts${path.sep}tests${path.sep}`, + `${path.sep}scripts${path.sep}lib${path.sep}packet-generalization-boundary.mjs`, + `${path.sep}scripts${path.sep}check-packet-generalization-boundary.mjs`, + `${path.sep}benches${path.sep}`, + `${path.sep}codestory-bench${path.sep}`, + `${path.sep}benchmarks${path.sep}`, +]); + +const BENCHMARK_DEPENDENCY_PATTERNS = Object.freeze([ + /benchmarks\//, + /codestory-bench/, + /language-expansion-holdout/, + /eval[_-]manifest/, + /task_manifest_snapshot/, + /expected_files\s*:/, + /expected_symbols\s*:/, + /expected_claims\s*:/, +]); + +function defaultRepoRoot() { + return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +} + +export function decodeAsciiByteArrayLiterals(source) { + const decoded = []; + const re = /\[\s*((?:\d{1,3}\s*,\s*)*\d{1,3})\s*\]/g; + let match; + while ((match = re.exec(source)) != null) { + const nums = match[1].split(",").map((part) => Number(part.trim())); + if (nums.length === 0 || nums.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) { + continue; + } + if (!nums.every((n) => (n >= 32 && n <= 126) || n === 9 || n === 10 || n === 13)) { + continue; + } + const text = String.fromCharCode(...nums); + if (/[A-Za-z]/.test(text)) { + decoded.push({ literal: match[0], text, index: match.index }); + } + } + return decoded; +} + +function isPermittedVocabularyPath(filePath, repoRoot) { + const relative = path.relative(repoRoot, filePath).split(path.sep).join("/"); + if (relative.startsWith("scripts/tests/") || relative.startsWith("scripts/lib/packet-generalization")) { + return true; + } + if (relative.startsWith("scripts/check-packet-generalization-boundary.mjs")) { + return true; + } + if (relative.includes("/tests/") || relative.endsWith("_test.rs") || relative.endsWith(".test.mjs")) { + return true; + } + if (relative.startsWith("benchmarks/") || relative.startsWith("crates/codestory-bench/")) { + return true; + } + // cfg(test) modules inside production files are still production scan targets; + // callers mask them before scanning when needed. Path-level permit is for + // dedicated test/tooling trees only. + void PERMITTED_VOCABULARY_PATH_FRAGMENTS; + return false; +} + +function listRustFiles(dir) { + const out = []; + if (!existsSync(dir)) return out; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + if (entry.name === "target" || entry.name === "node_modules") continue; + out.push(...listRustFiles(full)); + } else if (entry.isFile() && entry.name.endsWith(".rs")) { + out.push(full); + } + } + return out; +} + +/** Strip `#[cfg(test)]` item bodies for a conservative production view. */ +export function maskCfgTestItems(source) { + // Remove cfg(test) mod blocks and trailing test modules heuristically. + return source + .replace(/#\[cfg\(test\)\][\s\S]*?(?=\n(?:pub\s+)?(?:fn|struct|enum|impl|mod|const|type|use|#\[|$))/g, "\n") + .replace(/\nmod tests\s*\{[\s\S]*\}\s*$/m, "\n"); +} + +export function findBoundaryViolations(source, { filePath = "", repoRoot = defaultRepoRoot() } = {}) { + const findings = []; + const relative = filePath === "" + ? "" + : path.relative(repoRoot, filePath).split(path.sep).join("/"); + const productionView = maskCfgTestItems(source); + + if (!isPermittedVocabularyPath(filePath === "" ? path.join(repoRoot, "crates/codestory-agent/src/packet_terms.rs") : filePath, repoRoot) + || filePath === "") { + for (const pattern of BENCHMARK_DEPENDENCY_PATTERNS) { + if (pattern.test(productionView)) { + findings.push({ + kind: "benchmark_dependency", + file: relative, + detail: `matched ${pattern}`, + }); + } + } + + for (const anchor of HISTORICAL_EXPECTED_ANCHORS) { + if (productionView.includes(anchor)) { + findings.push({ + kind: "historical_expected_anchor", + file: relative, + detail: anchor, + }); + } + } + + for (const api of DELETED_TAXONOMY_APIS) { + const re = new RegExp(`\\b${api}\\b`); + if (re.test(productionView)) { + findings.push({ + kind: "deleted_taxonomy_api", + file: relative, + detail: api, + }); + } + } + + for (const decoded of decodeAsciiByteArrayLiterals(productionView)) { + const lower = decoded.text.toLowerCase(); + if (lower === "swr" || HISTORICAL_EXPECTED_ANCHORS.some((a) => a.toLowerCase() === lower)) { + findings.push({ + kind: "encoded_brand_literal", + file: relative, + detail: `${decoded.literal} => "${decoded.text}"`, + }); + } + } + } + + return findings; +} + +export function collectProductionPacketFiles(repoRoot = defaultRepoRoot()) { + const files = []; + for (const rel of PRODUCTION_SCAN_GLOBS) { + files.push(...listRustFiles(path.join(repoRoot, rel))); + } + return files.sort(); +} + +export function scanRepository(repoRoot = defaultRepoRoot()) { + const findings = []; + for (const filePath of collectProductionPacketFiles(repoRoot)) { + const source = readFileSync(filePath, "utf8"); + findings.push(...findBoundaryViolations(source, { filePath, repoRoot })); + } + return findings; +} + +export function formatFindings(findings) { + return findings + .map((f) => `packet-generalization-boundary: ${f.kind} ${f.detail} in ${f.file}`) + .join("\n"); +} + +export function runPacketGeneralizationBoundaryCheck(repoRoot = defaultRepoRoot()) { + if (!existsSync(repoRoot) || !statSync(repoRoot).isDirectory()) { + return { + exitCode: 2, + stdout: "", + stderr: `packet-generalization-boundary: repository root not found: ${repoRoot}\n`, + findings: [], + }; + } + const findings = scanRepository(repoRoot); + if (findings.length === 0) { + return { + exitCode: 0, + stdout: `packet-generalization-boundary: ok (${collectProductionPacketFiles(repoRoot).length} production packet file(s))\n`, + stderr: "", + findings, + }; + } + return { + exitCode: 1, + stdout: "", + stderr: `${formatFindings(findings)}\npacket-generalization-boundary: ${findings.length} violation(s)\n`, + findings, + }; +} diff --git a/scripts/tests/check-packet-generalization-boundary.test.mjs b/scripts/tests/check-packet-generalization-boundary.test.mjs new file mode 100644 index 000000000..d3cdda482 --- /dev/null +++ b/scripts/tests/check-packet-generalization-boundary.test.mjs @@ -0,0 +1,158 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; +import { + DELETED_TAXONOMY_APIS, + decodeAsciiByteArrayLiterals, + findBoundaryViolations, + runPacketGeneralizationBoundaryCheck, +} from "../lib/packet-generalization-boundary.mjs"; + +const repositoryRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../..", +); + +function writeTree(root, files) { + for (const [relative, contents] of Object.entries(files)) { + const full = path.join(root, relative); + fs.mkdirSync(path.dirname(full), { recursive: true }); + fs.writeFileSync(full, contents); + } +} + +test("decodes ASCII byte-array literals including encoded swr", () => { + const source = "let brand = [115, 119, 114]; let other = [100, 97, 114, 116];"; + const decoded = decodeAsciiByteArrayLiterals(source); + assert.deepEqual( + decoded.map((d) => d.text).sort(), + ["dart", "swr"], + ); +}); + +test("fixture contaminated head fails for taxonomy, cleanup, encoded brand, and holdout anchors", () => { + const contaminated = ` +pub fn packet_terms_indicate_hook_cache_flow(terms: &[String]) -> bool { + let encoded = [115, 119, 114]; + terms.iter().any(|t| t == "swr") +} +pub fn packet_drop_unrequested_markdown_siblings(rows: &mut Vec<()>) {} +pub fn packet_flow_requirements_for_terms(terms: &[String]) -> Vec<()> { vec![] } +pub fn append_flow_template_claims() {} +const HOLDOUT: &str = "language-expansion-holdout"; +const PATH: &str = "src/requests/api.py"; +`; + const findings = findBoundaryViolations(contaminated, { + filePath: path.join(repositoryRoot, "crates/codestory-agent/src/packet_terms.rs"), + repoRoot: repositoryRoot, + }); + const kinds = new Set(findings.map((f) => f.kind)); + assert.ok(kinds.has("deleted_taxonomy_api"), findings); + assert.ok(kinds.has("encoded_brand_literal"), findings); + assert.ok(kinds.has("historical_expected_anchor"), findings); + assert.ok( + findings.some((f) => f.detail.includes("packet_terms_indicate_hook_cache_flow")), + findings, + ); + assert.ok( + findings.some((f) => f.detail.includes("packet_drop_unrequested_markdown_siblings")), + findings, + ); +}); + +test("renaming a domain classifier while keeping benchmark-shaped behavior still fails", () => { + // Hostile rename: surface looks new, but still encodes the holdout brand and + // still implements a prompt→domain-flow classifier + cleanup pass. + const renamed = ` +pub fn packet_terms_indicate_cache_hook_pipeline(terms: &[String]) -> bool { + let brand = [115, 119, 114]; + std::str::from_utf8(&brand).unwrap(); + terms.iter().any(|t| t.contains("hook")) +} +pub fn packet_drop_unrequested_sibling_noise(rows: &mut Vec<()>) { + let _ = "dart-http-client-flow"; +} +`; + const findings = findBoundaryViolations(renamed, { + filePath: path.join(repositoryRoot, "crates/codestory-agent/src/packet_scoring.rs"), + repoRoot: repositoryRoot, + }); + assert.ok( + findings.some((f) => f.kind === "encoded_brand_literal"), + "encoded brand must still fail after rename", + ); + assert.ok( + findings.some((f) => f.kind === "historical_expected_anchor" && f.detail.includes("dart-http-client-flow")), + findings, + ); +}); + +test("clean generic seed / projection surface passes", () => { + const clean = ` +pub fn extract_packet_query_terms(question: &str) -> Vec { + question.split_whitespace().map(str::to_string).collect() +} +pub fn packet_citation_key(path: &str, start: u32, end: u32) -> String { + format!("{path}:{start}:{end}") +} +`; + const findings = findBoundaryViolations(clean, { + filePath: path.join(repositoryRoot, "crates/codestory-agent/src/packet_plan.rs"), + repoRoot: repositoryRoot, + }); + assert.deepEqual(findings, []); +}); + +test("isolated clean fixture repository passes the live scanner", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "packet-boundary-clean-")); + try { + writeTree(root, { + "crates/codestory-agent/src/packet_plan.rs": + "pub fn extract_packet_query_terms(q: &str) -> Vec { vec![q.to_string()] }\n", + "crates/codestory-runtime/src/agent/mod.rs": "pub mod packet_plan;\n", + }); + const result = runPacketGeneralizationBoundaryCheck(root); + assert.equal(result.exitCode, 0, result.stderr); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test("isolated contaminated fixture repository fails the live scanner", () => { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "packet-boundary-dirty-")); + try { + writeTree(root, { + "crates/codestory-agent/src/packet_terms.rs": + `pub fn packet_terms_indicate_hook_cache_flow(terms: &[String]) -> bool { let _ = [115,119,114]; true }\n`, + "crates/codestory-runtime/src/agent/orchestrator.rs": + "fn rank() { packet_drop_unrequested_markdown_siblings(); }\n", + }); + const result = runPacketGeneralizationBoundaryCheck(root); + assert.equal(result.exitCode, 1, result.stdout); + assert.match(result.stderr, /deleted_taxonomy_api|encoded_brand_literal/); + } finally { + fs.rmSync(root, { recursive: true, force: true }); + } +}); + +test("current integration head is contaminated (gate: fail-on-contaminated-head)", () => { + const result = runPacketGeneralizationBoundaryCheck(repositoryRoot); + assert.equal(result.exitCode, 1, "contaminated production head must fail the boundary checker"); + assert.ok(result.findings.length > 0); + const apis = new Set( + result.findings.filter((f) => f.kind === "deleted_taxonomy_api").map((f) => f.detail), + ); + assert.ok(apis.has("packet_flow_requirements_for_terms")); + assert.ok(apis.has("packet_terms_indicate_hook_cache_flow")); + assert.ok( + DELETED_TAXONOMY_APIS.some((api) => apis.has(api)), + "at least one deleted taxonomy API must still be present on contaminated head", + ); + assert.ok( + result.findings.some((f) => f.kind === "encoded_brand_literal"), + "encoded SWR detector must be visible on contaminated head", + ); +}); From e14f2009b63fd2dd9dbcbc8e275f96f486f794a1 Mon Sep 17 00:00:00 2001 From: Albert Najjar Date: Mon, 31 Aug 2026 08:23:22 -0400 Subject: [PATCH 02/13] add repository evidence plan module Introduce Stage B graph planning with frozen initial limits and unit coverage for empty, connected, and vocabulary-only inputs. Co-authored-by: Cursor --- crates/codestory-agent/src/lib.rs | 1 + .../src/repository_evidence_plan.rs | 519 ++++++++++++++++++ 2 files changed, 520 insertions(+) create mode 100644 crates/codestory-agent/src/repository_evidence_plan.rs diff --git a/crates/codestory-agent/src/lib.rs b/crates/codestory-agent/src/lib.rs index c91173f96..e3fbaae42 100644 --- a/crates/codestory-agent/src/lib.rs +++ b/crates/codestory-agent/src/lib.rs @@ -99,6 +99,7 @@ pub mod packet_terms; pub mod pinned_reader; pub mod planning; pub mod profiles; +pub mod repository_evidence_plan; pub mod text; pub mod trail; pub use pinned_reader::{ContinuationRefusal, PinnedReader, admit_continuation_probe}; diff --git a/crates/codestory-agent/src/repository_evidence_plan.rs b/crates/codestory-agent/src/repository_evidence_plan.rs new file mode 100644 index 000000000..0d3a451d1 --- /dev/null +++ b/crates/codestory-agent/src/repository_evidence_plan.rs @@ -0,0 +1,519 @@ +//! Repository-derived evidence planning (Stage B). +//! +//! After runtime retrieves seed citations and a bounded typed relationship +//! graph, this module selects material nodes/edges from repository structure +//! alone. It never invents domain stage taxonomies from prompt vocabulary. + +use codestory_contracts::api::{ + AgentCitationDto, EdgeId, EdgeKind, GraphEdgeDto, NodeId, PacketTaskClassDto, +}; +use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque}; + +/// Initial planner constants. Mutable only during the visible metamorphic +/// phase; freeze before sealed holdout creation. +pub const DEFAULT_REPOSITORY_EVIDENCE_LIMITS: RepositoryEvidenceLimits = RepositoryEvidenceLimits { + max_seed_nodes: 12, + max_candidate_nodes: 256, + max_candidate_edges: 512, + max_depth: 4, + max_relation_paths: 32, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct RepositoryEvidenceLimits { + pub max_seed_nodes: usize, + pub max_candidate_nodes: usize, + pub max_candidate_edges: usize, + pub max_depth: usize, + pub max_relation_paths: usize, +} + +impl Default for RepositoryEvidenceLimits { + fn default() -> Self { + DEFAULT_REPOSITORY_EVIDENCE_LIMITS + } +} + +#[derive(Debug, Clone)] +pub struct RepositoryEvidenceInput<'a> { + pub question: &'a str, + pub task_class: PacketTaskClassDto, + pub seeds: &'a [AgentCitationDto], + pub relations: &'a [GraphEdgeDto], +} + +/// A repository-grounded objective. Identifiers refer only to graph entities. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryEvidenceObjective { + pub kind: RepositoryEvidenceObjectiveKind, + pub node_ids: Vec, + pub edge_ids: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RepositoryEvidenceObjectiveKind { + /// Explicit prompt anchor resolved to a repository node. + ResolvedAnchor, + /// Shortest retained relationship path connecting distinct anchors. + RelationPath, + /// Implementation / membership relationship behind a selected anchor. + ImplementationRelation, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RepositoryEvidenceGap { + pub kind: RepositoryEvidenceGapKind, + pub detail: String, + pub node_ids: Vec, + pub edge_ids: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RepositoryEvidenceGapKind { + /// No seed citations resolved from the prompt. + UnresolvedAnchors, + /// Seeds exist but no typed relationship supports the requested path. + MissingRelation, + /// Search truncated by planner limits; continuation may name remainder. + TruncatedSearch, + /// Ambiguous or incomplete graph; do not assert absence. + Unknown, +} + +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct RepositoryEvidencePlan { + pub material_node_ids: Vec, + pub material_edge_ids: Vec, + pub objectives: Vec, + pub uncovered: Vec, +} + +pub fn build_repository_evidence_plan( + input: RepositoryEvidenceInput<'_>, + limits: RepositoryEvidenceLimits, +) -> RepositoryEvidencePlan { + let _question = input.question; // retained for future generic anchor parsing only + let mut plan = RepositoryEvidencePlan::default(); + + let seed_nodes = unique_seed_nodes(input.seeds, limits.max_seed_nodes); + if seed_nodes.is_empty() { + plan.uncovered.push(RepositoryEvidenceGap { + kind: RepositoryEvidenceGapKind::UnresolvedAnchors, + detail: "no repository seeds resolved from the prompt".into(), + node_ids: Vec::new(), + edge_ids: Vec::new(), + }); + return plan; + } + + for node_id in &seed_nodes { + plan.objectives.push(RepositoryEvidenceObjective { + kind: RepositoryEvidenceObjectiveKind::ResolvedAnchor, + node_ids: vec![node_id.clone()], + edge_ids: Vec::new(), + }); + push_unique_node(&mut plan.material_node_ids, node_id.clone()); + } + + let preferred = preferred_edge_kinds(input.task_class); + let adjacency = build_adjacency(input.relations, &preferred, limits.max_candidate_edges); + + let mut path_count = 0usize; + let mut truncated = false; + for (seed_index, start) in seed_nodes.iter().enumerate() { + for end in seed_nodes.iter().skip(seed_index + 1) { + if path_count >= limits.max_relation_paths { + truncated = true; + break; + } + match shortest_path(start, end, &adjacency, limits.max_depth, limits.max_candidate_nodes) + { + Some(path) => { + path_count += 1; + for node in &path.nodes { + push_unique_node(&mut plan.material_node_ids, node.clone()); + } + for edge in &path.edges { + push_unique_edge(&mut plan.material_edge_ids, edge.clone()); + } + plan.objectives.push(RepositoryEvidenceObjective { + kind: RepositoryEvidenceObjectiveKind::RelationPath, + node_ids: path.nodes, + edge_ids: path.edges, + }); + } + None => { + // Missing path between two seeds is unknown, not absence. + plan.uncovered.push(RepositoryEvidenceGap { + kind: RepositoryEvidenceGapKind::MissingRelation, + detail: "no retained typed path between resolved anchors".into(), + node_ids: vec![start.clone(), end.clone()], + edge_ids: Vec::new(), + }); + } + } + } + if truncated { + break; + } + } + + // Implementation relations incident to seeds (membership / override / etc.). + for edge in input.relations.iter().take(limits.max_candidate_edges) { + if !is_implementation_kind(edge.kind, input.task_class) { + continue; + } + let touches_seed = seed_nodes.iter().any(|n| n == &edge.source || n == &edge.target); + if !touches_seed { + continue; + } + push_unique_node(&mut plan.material_node_ids, edge.source.clone()); + push_unique_node(&mut plan.material_node_ids, edge.target.clone()); + push_unique_edge(&mut plan.material_edge_ids, edge.id.clone()); + plan.objectives.push(RepositoryEvidenceObjective { + kind: RepositoryEvidenceObjectiveKind::ImplementationRelation, + node_ids: vec![edge.source.clone(), edge.target.clone()], + edge_ids: vec![edge.id.clone()], + }); + if plan.objectives.len() > limits.max_relation_paths.saturating_mul(2) { + truncated = true; + break; + } + } + + if seed_nodes.len() >= 2 + && plan + .objectives + .iter() + .all(|o| o.kind == RepositoryEvidenceObjectiveKind::ResolvedAnchor) + { + plan.uncovered.push(RepositoryEvidenceGap { + kind: RepositoryEvidenceGapKind::Unknown, + detail: "anchors resolved but no repository relationship selected".into(), + node_ids: seed_nodes.clone(), + edge_ids: Vec::new(), + }); + } + + if truncated { + plan.uncovered.push(RepositoryEvidenceGap { + kind: RepositoryEvidenceGapKind::TruncatedSearch, + detail: "repository evidence search hit planner limits".into(), + node_ids: Vec::new(), + edge_ids: Vec::new(), + }); + } + + // Domain vocabulary in the question never creates objectives by itself. + // Objectives exist only from seeds/relations above. + let _ = input.task_class; + plan +} + +fn unique_seed_nodes(seeds: &[AgentCitationDto], max_seed_nodes: usize) -> Vec { + let mut out = Vec::new(); + let mut seen = BTreeSet::new(); + for seed in seeds { + if seen.insert(seed.node_id.clone()) { + out.push(seed.node_id.clone()); + } + if out.len() >= max_seed_nodes { + break; + } + } + out +} + +fn preferred_edge_kinds(task_class: PacketTaskClassDto) -> HashSet { + let kinds: &[EdgeKind] = match task_class { + PacketTaskClassDto::ArchitectureExplanation => &[ + EdgeKind::CALL, + EdgeKind::MEMBER, + EdgeKind::INHERITANCE, + EdgeKind::OVERRIDE, + EdgeKind::IMPORT, + EdgeKind::INCLUDE, + ], + PacketTaskClassDto::BugLocalization => &[ + EdgeKind::CALL, + EdgeKind::USAGE, + EdgeKind::TYPE_USAGE, + EdgeKind::MEMBER, + EdgeKind::OVERRIDE, + ], + PacketTaskClassDto::ChangeImpact => &[ + EdgeKind::CALL, + EdgeKind::USAGE, + EdgeKind::TYPE_USAGE, + EdgeKind::IMPORT, + EdgeKind::INCLUDE, + ], + PacketTaskClassDto::RouteTracing => &[EdgeKind::CALL], + PacketTaskClassDto::SymbolOwnership => { + &[EdgeKind::MEMBER, EdgeKind::OVERRIDE, EdgeKind::INHERITANCE] + } + PacketTaskClassDto::DataFlow => &[ + EdgeKind::CALL, + EdgeKind::USAGE, + EdgeKind::TYPE_USAGE, + EdgeKind::MEMBER, + ], + PacketTaskClassDto::EditPlanning => &[ + EdgeKind::CALL, + EdgeKind::USAGE, + EdgeKind::TYPE_USAGE, + EdgeKind::MEMBER, + EdgeKind::OVERRIDE, + EdgeKind::INHERITANCE, + EdgeKind::IMPORT, + EdgeKind::INCLUDE, + ], + }; + kinds.iter().copied().collect() +} + +fn is_implementation_kind(kind: EdgeKind, task_class: PacketTaskClassDto) -> bool { + preferred_edge_kinds(task_class).contains(&kind) + && matches!( + kind, + EdgeKind::MEMBER | EdgeKind::OVERRIDE | EdgeKind::INHERITANCE | EdgeKind::CALL + ) +} + +fn inbound_preferred(task_class: PacketTaskClassDto) -> bool { + matches!( + task_class, + PacketTaskClassDto::ChangeImpact | PacketTaskClassDto::EditPlanning + ) +} + +#[derive(Debug, Clone)] +struct AdjEdge { + to: NodeId, + edge_id: EdgeId, +} + +fn build_adjacency( + relations: &[GraphEdgeDto], + preferred: &HashSet, + max_edges: usize, +) -> BTreeMap> { + let mut adj: BTreeMap> = BTreeMap::new(); + for edge in relations.iter().take(max_edges) { + if !preferred.contains(&edge.kind) { + continue; + } + adj.entry(edge.source.clone()) + .or_default() + .push(AdjEdge { + to: edge.target.clone(), + edge_id: edge.id.clone(), + }); + // Undirected expansion for ownership/impact unless strictly ordered CALL + // route tracing, which still benefits from reverse edges when searching + // paths between anchors. + adj.entry(edge.target.clone()) + .or_default() + .push(AdjEdge { + to: edge.source.clone(), + edge_id: edge.id.clone(), + }); + } + let _ = inbound_preferred; // direction ranking reserved for future scoring + adj +} + +#[derive(Debug, Clone)] +struct PathResult { + nodes: Vec, + edges: Vec, +} + +fn shortest_path( + start: &NodeId, + end: &NodeId, + adjacency: &BTreeMap>, + max_depth: usize, + max_nodes: usize, +) -> Option { + if start == end { + return Some(PathResult { + nodes: vec![start.clone()], + edges: Vec::new(), + }); + } + let mut queue = VecDeque::new(); + let mut visited = BTreeSet::new(); + // pred: node -> (previous node, edge used) + let mut pred: BTreeMap = BTreeMap::new(); + queue.push_back((start.clone(), 0usize)); + visited.insert(start.clone()); + while let Some((node, depth)) = queue.pop_front() { + if depth >= max_depth { + continue; + } + for edge in adjacency.get(&node).into_iter().flatten() { + if !visited.insert(edge.to.clone()) { + continue; + } + pred.insert(edge.to.clone(), (node.clone(), edge.edge_id.clone())); + if &edge.to == end { + return Some(reconstruct_path(start, end, &pred)); + } + if visited.len() >= max_nodes { + return None; + } + queue.push_back((edge.to.clone(), depth + 1)); + } + } + None +} + +fn reconstruct_path( + start: &NodeId, + end: &NodeId, + pred: &BTreeMap, +) -> PathResult { + let mut nodes = vec![end.clone()]; + let mut edges = Vec::new(); + let mut current = end.clone(); + while ¤t != start { + let (prev, edge_id) = pred.get(¤t).expect("path predecessor"); + edges.push(edge_id.clone()); + nodes.push(prev.clone()); + current = prev.clone(); + } + nodes.reverse(); + edges.reverse(); + PathResult { nodes, edges } +} + +fn push_unique_node(nodes: &mut Vec, node: NodeId) { + if !nodes.iter().any(|n| n == &node) { + nodes.push(node); + } +} + +fn push_unique_edge(edges: &mut Vec, edge: EdgeId) { + if !edges.iter().any(|e| e == &edge) { + edges.push(edge); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use codestory_contracts::api::{NodeKind, SearchHitOrigin}; + + fn citation(id: &str, name: &str) -> AgentCitationDto { + AgentCitationDto { + node_id: NodeId(id.into()), + display_name: name.into(), + kind: NodeKind::FUNCTION, + file_path: Some(format!("src/{name}.rs")), + line: Some(1), + score: 1.0, + origin: SearchHitOrigin::IndexedSymbol, + target: None, + resolvable: true, + subgraph_id: None, + evidence_edge_ids: Vec::new(), + retrieval_score_breakdown: None, + evidence_tier: None, + evidence_producer: None, + resolution_status: None, + loss_reason: None, + coverage_role: None, + eligible_for_sufficiency: None, + source_excerpt: None, + } + } + + fn call_edge(id: &str, source: &str, target: &str) -> GraphEdgeDto { + GraphEdgeDto { + id: EdgeId(id.into()), + source: NodeId(source.into()), + target: NodeId(target.into()), + kind: EdgeKind::CALL, + confidence: Some(1.0), + certainty: Some("certain".into()), + callsite_identity: None, + candidate_targets: Vec::new(), + } + } + + #[test] + fn empty_graph_yields_unresolved_or_unknown_gaps() { + let plan = build_repository_evidence_plan( + RepositoryEvidenceInput { + question: "Explain the client cache mapper animation flow", + task_class: PacketTaskClassDto::ArchitectureExplanation, + seeds: &[], + relations: &[], + }, + RepositoryEvidenceLimits::default(), + ); + assert!(plan.objectives.is_empty()); + assert!(plan.material_node_ids.is_empty()); + assert!( + plan.uncovered + .iter() + .any(|g| g.kind == RepositoryEvidenceGapKind::UnresolvedAnchors) + ); + } + + #[test] + fn two_seeds_with_call_edge_select_material_path() { + let seeds = [citation("n1", "foo"), citation("n2", "bar")]; + let relations = [call_edge("e1", "n1", "n2")]; + let plan = build_repository_evidence_plan( + RepositoryEvidenceInput { + question: "Trace foo calling bar", + task_class: PacketTaskClassDto::RouteTracing, + seeds: &seeds, + relations: &relations, + }, + RepositoryEvidenceLimits::default(), + ); + assert!( + plan.objectives + .iter() + .any(|o| o.kind == RepositoryEvidenceObjectiveKind::RelationPath) + ); + assert!(plan.material_edge_ids.iter().any(|e| e.0 == "e1")); + assert!(plan.material_node_ids.iter().any(|n| n.0 == "n1")); + assert!(plan.material_node_ids.iter().any(|n| n.0 == "n2")); + assert!( + !plan + .objectives + .iter() + .any(|o| format!("{o:?}").contains("client_transport")) + ); + } + + #[test] + fn domain_vocabulary_without_edges_creates_no_relation_objectives() { + let seeds = [citation("n1", "Client"), citation("n2", "Cache")]; + let plan = build_repository_evidence_plan( + RepositoryEvidenceInput { + question: "Explain how the client cache formatter mapper request animation works", + task_class: PacketTaskClassDto::ArchitectureExplanation, + seeds: &seeds, + relations: &[], + }, + RepositoryEvidenceLimits::default(), + ); + assert!( + plan.objectives + .iter() + .all(|o| o.kind == RepositoryEvidenceObjectiveKind::ResolvedAnchor) + ); + assert!(plan.material_edge_ids.is_empty()); + assert!( + plan.uncovered.iter().any(|g| matches!( + g.kind, + RepositoryEvidenceGapKind::MissingRelation | RepositoryEvidenceGapKind::Unknown + )) + ); + } +} From 5ec094917f629cc54727943d85fb630a60701c6e Mon Sep 17 00:00:00 2001 From: Albert Najjar Date: Mon, 31 Aug 2026 08:53:47 -0400 Subject: [PATCH 03/13] replace contaminated packet planner with repository evidence path Delete domain flow classifiers, cleanup passes, and flow-template claim surfaces; keep generic seeds and Stage B repository evidence planning, and flip the generalization boundary gate to expect a clean head. Co-authored-by: Cursor --- crates/codestory-agent/src/packet_claims.rs | 254 +- .../src/packet_evidence_carriers.rs | 25 +- .../src/packet_flow_requirements.rs | 5513 +---------------- .../codestory-agent/src/packet_obligations.rs | 4269 +------------ crates/codestory-agent/src/packet_plan.rs | 118 +- .../src/packet_required_probes.rs | 534 +- crates/codestory-agent/src/packet_scoring.rs | 1868 +----- crates/codestory-agent/src/packet_terms.rs | 841 +-- crates/codestory-agent/src/text.rs | 2 +- .../tests/packet_generalization_boundary.rs | 269 +- .../src/agent/orchestrator.rs | 161 +- .../src/agent/packet_batch.rs | 3 +- .../src/agent/packet_budget.rs | 14 +- .../src/agent/packet_candidate.rs | 76 +- .../src/agent/packet_trace.rs | 20 +- .../src/agent/retrieval_primary.rs | 70 +- .../lib/packet-generalization-boundary.mjs | 91 +- ...ck-packet-generalization-boundary.test.mjs | 22 +- 18 files changed, 676 insertions(+), 13474 deletions(-) diff --git a/crates/codestory-agent/src/packet_claims.rs b/crates/codestory-agent/src/packet_claims.rs index 4e9728f92..a818bcff7 100644 --- a/crates/codestory-agent/src/packet_claims.rs +++ b/crates/codestory-agent/src/packet_claims.rs @@ -1,9 +1,5 @@ #[cfg(any(test, feature = "test-support"))] -use crate::eval_probes::{ - eval_citation_shaped_claim, eval_flow_template_claims, - eval_indexing_storage_flow_template_claims, eval_probes_enabled, - eval_supporting_claim_flow_sentence, -}; +use crate::eval_probes::{eval_citation_shaped_claim, eval_supporting_claim_flow_sentence}; use crate::packet_evidence::{ citation_sufficiency_eligible, evidence_resolution_for_citation, evidence_tier_for_citation, }; @@ -62,13 +58,7 @@ pub fn packet_supported_claims_with_telemetry( let prefer_primary_sources = !query_mentions_non_primary_source(&answer.prompt); let citations = answer.citations.clone(); - append_flow_template_claims( - &answer.prompt, - &citations, - &mut claims, - &mut seen_claims, - &mut telemetry, - ); + let before_role_claims = claims.len(); append_ranked_citation_claims( &answer.prompt, @@ -138,248 +128,8 @@ fn packet_citation_is_diagnostic_only(citation: &AgentCitationDto) -> bool { ) } -pub fn append_flow_template_claims( - prompt: &str, - citations: &[AgentCitationDto], - claims: &mut Vec, - seen: &mut HashSet, - telemetry: &mut PacketClaimTelemetry, -) { - let normalized_prompt = normalize_identifier(prompt); - - let phase = ClaimSourcePhase::start(claims); - packet_append_event_output_flow_template_claims(&normalized_prompt, citations, claims, seen); - packet_append_indexing_pipeline_flow_template_claims(prompt, citations, claims, seen); - phase.finish(PacketClaimSource::FlowTemplate, claims, telemetry); - - #[cfg(any(test, feature = "test-support"))] - if eval_probes_enabled() { - let phase = ClaimSourcePhase::start(claims); - for (claim, claim_citations) in - eval_indexing_storage_flow_template_claims(prompt, citations) - { - packet_push_flow_template_claim_with_citations(claims, seen, &claim, claim_citations); - } - for (claim, citation) in eval_flow_template_claims(&normalized_prompt, citations) { - packet_push_flow_template_claim(claims, seen, &claim, Some(citation)); - } - phase.finish(PacketClaimSource::EvalProbe, claims, telemetry); - } -} - /// Counts claims one assembly layer actually added, so `claim_source` totals describe the /// packet that shipped rather than what a layer offered before dedupe and the claim cap. -struct ClaimSourcePhase { - before: usize, -} - -impl ClaimSourcePhase { - fn start(claims: &[PacketClaimDto]) -> Self { - Self { - before: claims.len(), - } - } - - fn finish( - self, - source: PacketClaimSource, - claims: &[PacketClaimDto], - telemetry: &mut PacketClaimTelemetry, - ) { - telemetry.record_claim_source(source, claims.len().saturating_sub(self.before)); - } -} - -fn packet_append_event_output_flow_template_claims( - normalized_prompt: &str, - citations: &[AgentCitationDto], - claims: &mut Vec, - seen: &mut HashSet, -) { - if (normalized_prompt.contains("json") || normalized_prompt.contains("jsonl")) - && (normalized_prompt.contains("event") || normalized_prompt.contains("output")) - && let Some(event_output_citation) = citations.iter().find(|citation| { - packet_evidence_role(citation) == Some(PacketEvidenceRole::EventOutputProcessing) - }) - { - packet_push_flow_template_claim( - claims, - seen, - "Event-output processing evidence describes how structured runtime events are serialized for JSON/JSONL output.", - Some(event_output_citation.clone()), - ); - } -} - -fn packet_append_indexing_pipeline_flow_template_claims( - prompt: &str, - citations: &[AgentCitationDto], - claims: &mut Vec, - seen: &mut HashSet, -) { - let normalized_prompt = normalize_identifier(prompt); - let indexing_prompt = normalized_prompt.contains("indexing") - || normalized_prompt.contains("indexed") - || normalized_prompt.contains("indexer") - || normalized_prompt.contains("indexcommand"); - if !(indexing_prompt - && normalized_prompt.contains("runtime") - && (normalized_prompt.contains("workspace") - || normalized_prompt.contains("sourcefile") - || normalized_prompt.contains("filediscovery")) - && (normalized_prompt.contains("persistence") || normalized_prompt.contains("store")) - && normalized_prompt.contains("snapshot")) - { - return; - } - - let cli_entry = packet_citation_matching_role(citations, PacketEvidenceRole::CommandEntrypoint); - let runtime_entry = - packet_citation_matching_role(citations, PacketEvidenceRole::RuntimeOrchestration); - if let Some(runtime_entry) = &runtime_entry { - let mut claim_citations = Vec::new(); - if let Some(cli_entry) = cli_entry { - claim_citations.push(cli_entry.clone()); - } - claim_citations.push(runtime_entry.clone()); - packet_push_flow_template_claim_with_citations( - claims, - seen, - "The packet carries independent indexing-entrypoint and runtime-orchestration source anchors.", - claim_citations, - ); - } - - let workspace_plan = - packet_citation_matching_role(citations, PacketEvidenceRole::WorkspaceDiscoveryAndPlanning); - if let Some(runtime_entry) = &runtime_entry { - let mut claim_citations = vec![runtime_entry.clone()]; - if let Some(workspace_plan) = &workspace_plan { - claim_citations.push(workspace_plan.clone()); - } - packet_push_flow_template_claim_with_citations( - claims, - seen, - "The packet carries independent runtime-orchestration and workspace-planning source anchors.", - claim_citations, - ); - } - - if let Some(workspace_plan) = &workspace_plan { - packet_push_flow_template_claim( - claims, - seen, - "Workspace discovery evidence plans source-file discovery and refresh work.", - Some(workspace_plan.clone()), - ); - } - - let index_file = packet_citation_matching_role(citations, PacketEvidenceRole::SymbolExtraction); - if let Some(index_file) = index_file { - packet_push_flow_template_claim( - claims, - seen, - "Symbol extraction evidence builds graph nodes, edges, occurrences, and related source data.", - Some(index_file), - ); - } - - let storage_flush = packet_citation_matching_role( - citations, - PacketEvidenceRole::PersistenceAndSearchProjection, - ); - let search_projection = storage_flush.clone(); - if storage_flush.is_some() || search_projection.is_some() { - let mut claim_citations = Vec::new(); - if let Some(storage_flush) = storage_flush { - claim_citations.push(storage_flush.clone()); - } - if let Some(search_projection) = search_projection { - claim_citations.push(search_projection.clone()); - } - packet_push_flow_template_claim_with_citations( - claims, - seen, - "Persistence evidence stores graph/file data and rebuilds query/search projections.", - claim_citations, - ); - } - - if let Some(snapshot_refresh) = - packet_citation_matching_role(citations, PacketEvidenceRole::SnapshotRefresh) - { - packet_push_flow_template_claim( - claims, - seen, - "Snapshot refresh evidence updates read models after persisted graph changes.", - Some(snapshot_refresh.clone()), - ); - } -} - -fn packet_citation_matching_role( - citations: &[AgentCitationDto], - role: PacketEvidenceRole, -) -> Option { - let matching = citations - .iter() - .filter(|citation| packet_evidence_role(citation) == Some(role)); - if role == PacketEvidenceRole::SymbolExtraction { - return matching - .min_by_key(|citation| packet_symbol_extraction_witness_rank(citation)) - .cloned(); - } - matching.into_iter().next().cloned() -} - -fn packet_symbol_extraction_witness_rank(citation: &AgentCitationDto) -> u8 { - let display = normalize_identifier(&citation.display_name); - if display == "indexfile" || display.ends_with("indexfile") { - 0 - } else if display.contains("extract") || display.contains("symbol") { - 1 - } else if display.contains("indexer") { - 2 - } else { - 3 - } -} - -fn packet_push_flow_template_claim( - claims: &mut Vec, - seen: &mut HashSet, - claim_text: &str, - citation: Option, -) { - packet_push_flow_template_claim_with_citations( - claims, - seen, - claim_text, - citation.map(|value| vec![value]).unwrap_or_default(), - ); -} - -fn packet_push_flow_template_claim_with_citations( - claims: &mut Vec, - seen: &mut HashSet, - claim_text: &str, - citations: Vec, -) { - let key = normalize_identifier(claim_text); - if key.is_empty() || !seen.insert(key) { - return; - } - claims.push(PacketClaimDto { - claim: claim_text.to_string(), - required_obligation_ids: Vec::new(), - required_obligation_kinds: Vec::new(), - proof_status: None, - required_evidence_role: None, - citations, - coverage_role: Some("flow template".to_string()), - eligible_for_sufficiency: Some(false), - }); -} pub fn append_ranked_citation_claims( prompt: &str, diff --git a/crates/codestory-agent/src/packet_evidence_carriers.rs b/crates/codestory-agent/src/packet_evidence_carriers.rs index 90f69bb0f..78ccfa161 100644 --- a/crates/codestory-agent/src/packet_evidence_carriers.rs +++ b/crates/codestory-agent/src/packet_evidence_carriers.rs @@ -54,10 +54,7 @@ //! every other carrier here reads it. use crate::packet_scoring::{normalize_identifier, packet_display_path}; -use crate::packet_terms::{ - packet_terms_indicate_server_request_dispatch_flow, - packet_terms_indicate_server_route_dispatch_flow, -}; + use codestory_contracts::api::{AgentCitationDto, NodeKind}; fn terminal(citation: &AgentCitationDto) -> String { @@ -544,19 +541,10 @@ pub fn citation_owns_server_request_handler_entrypoint(citation: &AgentCitationD /// a request through handlers. Route-group helpers often outscore that /// callable on raw lexical overlap. pub fn packet_server_dispatch_callable_rank_bonus( - citation: &AgentCitationDto, - terms: &[String], + _citation: &AgentCitationDto, + _terms: &[String], ) -> f32 { - if !(packet_terms_indicate_server_route_dispatch_flow(terms) - || packet_terms_indicate_server_request_dispatch_flow(terms)) - { - return 0.0; - } - if citation_owns_server_request_dispatch(citation) { - 6.0 - } else { - 0.0 - } + 0.0 } /// The response-side callable that leaves a server handler for its writer or transport. @@ -3075,7 +3063,7 @@ mod tests { let dispatch_terms = crate::packet_terms::packet_probe_terms( "Trace how an HTTP server routes an incoming request through route registration, request handler dispatch, and response finalization.", ); - assert!( + assert_eq!( packet_server_dispatch_callable_rank_bonus( &citation( "ServerEngine.handleHTTPRequest", @@ -3083,7 +3071,8 @@ mod tests { NodeKind::METHOD, ), &dispatch_terms, - ) > 0.0 + ), + 0.0 ); assert_eq!( packet_server_dispatch_callable_rank_bonus( diff --git a/crates/codestory-agent/src/packet_flow_requirements.rs b/crates/codestory-agent/src/packet_flow_requirements.rs index a7ae79e02..a550ab009 100644 --- a/crates/codestory-agent/src/packet_flow_requirements.rs +++ b/crates/codestory-agent/src/packet_flow_requirements.rs @@ -1,72 +1,12 @@ -//! Generic packet flow requirements shared by planning, probes, and sufficiency. +//! Packet flow-requirement types retained for obligation/proof helpers. +//! Domain stage lists and prompt→flow dispatchers were deleted in Phase 3. use crate::packet_evidence_carriers::{ - SEARCH_EVIDENCE_CLASSIFICATION_ACTIONS, SEARCH_EVIDENCE_OUTPUT_ACTIONS, - citation_may_start_command_event_loop_exact_boundary, citation_owns_buffer_read_write, - citation_owns_buffer_storage, citation_owns_client_adapter_selection, - citation_owns_client_public_facade_helper, citation_owns_client_request_dispatch, - citation_owns_client_request_entrypoint, citation_owns_client_request_finalization, - citation_owns_client_request_method, citation_owns_client_response_materialization, - citation_owns_client_transport_send, citation_owns_command_event_loop_driver, - citation_owns_command_router, citation_owns_css_animation_entrypoint, - citation_owns_css_animation_structure, citation_owns_css_structure, - citation_owns_form_custom_validation, citation_owns_form_native_constraint, - citation_owns_form_submit_guard, citation_owns_format_arguments, - citation_owns_formatter_fallback, citation_owns_hook_cache_helper, - citation_owns_hook_key_serialization, citation_owns_hook_mutation_flow, - citation_owns_hook_public_export, citation_owns_html_app_shell, - citation_owns_log_handler_processing, citation_owns_log_record_creation, - citation_owns_mapper_configuration, citation_owns_mapper_execution, - citation_owns_search_argument_planning, citation_owns_search_candidate_traversal, - citation_owns_search_evidence_classification, citation_owns_search_evidence_output, - citation_owns_search_haystack_construction, citation_owns_search_matcher_setup, - citation_owns_search_printer_setup, citation_owns_search_searcher_setup, - citation_owns_search_worker_construction, citation_owns_server_request_dispatch, - citation_owns_server_request_entrypoint, citation_owns_server_request_handler_entrypoint, - citation_owns_server_response_terminal, citation_owns_server_route_match_dispatch, - citation_owns_server_route_registration, citation_owns_shell_completion, - citation_owns_shell_function_dispatch, citation_owns_shell_installer_bootstrap, - citation_owns_site_lifecycle, citation_owns_site_reader, citation_owns_site_terminal, - citation_owns_string_blank_predicate, citation_owns_string_empty_predicate, - citation_owns_string_region_handoff, client_public_facade_successor_call_target, - client_request_dispatch_predecessor_call_source, client_request_dispatch_successor_call_target, - client_request_entrypoint_call_target, command_event_loop_driver_call_target, - command_router_call_target, flow_belongs_to_client_request, flow_belongs_to_command_server, - flow_belongs_to_indexing, flow_belongs_to_network_input, flow_belongs_to_request_terminal, - flow_belongs_to_search, flow_belongs_to_server_request, flow_belongs_to_sql_schema, - flow_belongs_to_url_session, server_handler_chain_call_target, - server_request_dispatch_call_target, server_request_entrypoint_call_target, - server_response_terminal_call_target, server_route_insertion_call_target, - server_route_lookup_call_target, -}; -use crate::packet_evidence_roles::{ - PacketEvidenceRole, packet_citation_owns_interceptor_management, packet_evidence_role, -}; -use crate::packet_proof_atoms::{ - CSS_ANIMATION_FLOW_PROOF, FlowProofSpec, LOG_HANDLER_FLOW_PROOF, MAPPER_PLAN_FLOW_PROOF, -}; -use crate::packet_terms::{ - packet_terms_have_any, packet_terms_indicate_buffered_io_flow, - packet_terms_indicate_client_send_flow, packet_terms_indicate_command_dispatch_flow, - packet_terms_indicate_command_event_loop_flow, - packet_terms_indicate_command_server_bootstrap_flow, - packet_terms_indicate_event_loop_command_flow, packet_terms_indicate_form_validation_flow, - packet_terms_indicate_full_outbound_request_flow, packet_terms_indicate_hook_cache_flow, - packet_terms_indicate_html_css_template_structure_flow, packet_terms_indicate_indexing_flow, - packet_terms_indicate_log_record_handler_flow, - packet_terms_indicate_mapper_configuration_plan_flow, - packet_terms_indicate_network_command_input_flow, packet_terms_indicate_request_dispatch_flow, - packet_terms_indicate_runtime_formatting_flow, packet_terms_indicate_search_execution_flow, - packet_terms_indicate_server_request_dispatch_flow, - packet_terms_indicate_server_route_dispatch_flow, - packet_terms_indicate_shell_install_dispatch_flow, packet_terms_indicate_site_build_phase_flow, - packet_terms_indicate_sql_schema_flow, packet_terms_indicate_string_predicate_flow, - packet_terms_indicate_stylesheet_animation_flow, - packet_terms_indicate_url_session_request_flow, prompt_search_terms, -}; -use codestory_contracts::api::{ - AgentCitationDto, EdgeKind, GraphEdgeDto, NodeKind, PacketTaskClassDto, + citation_may_start_command_event_loop_exact_boundary, command_event_loop_driver_call_target, }; +use crate::packet_evidence_roles::{PacketEvidenceRole, packet_evidence_role}; +use crate::packet_proof_atoms::FlowProofSpec; +use codestory_contracts::api::{AgentCitationDto, EdgeKind, GraphEdgeDto, NodeKind}; const CALLABLE_NODE_KINDS: &[NodeKind] = &[NodeKind::FUNCTION, NodeKind::METHOD, NodeKind::MACRO]; const BEHAVIORAL_OWNER_NODE_KINDS: &[NodeKind] = &[ @@ -475,5444 +415,3 @@ impl FlowRequirement { } } -pub fn packet_flow_requirements_for_terms( - terms: &[String], - task_class: PacketTaskClassDto, -) -> Vec { - if !matches!( - task_class, - PacketTaskClassDto::ArchitectureExplanation - | PacketTaskClassDto::DataFlow - | PacketTaskClassDto::ChangeImpact - | PacketTaskClassDto::RouteTracing - | PacketTaskClassDto::EditPlanning - ) { - return Vec::new(); - } - - let mut requirements = Vec::new(); - if packet_terms_indicate_indexing_flow(terms) { - requirements.extend_from_slice(INDEXING_FLOW); - } - let server_request_dispatch = packet_terms_indicate_server_request_dispatch_flow(terms); - let server_route_dispatch = packet_terms_indicate_server_route_dispatch_flow(terms); - let client_request_dispatch = packet_terms_indicate_request_dispatch_flow(terms); - let full_outbound_request = packet_terms_indicate_full_outbound_request_flow(terms); - if server_request_dispatch { - requirements.extend_from_slice(SERVER_REQUEST_DISPATCH_FLOW); - } else if server_route_dispatch { - let response_terminal_requested = packet_terms_have_any(terms, &["response", "responses"]); - requirements.extend( - SERVER_REQUEST_DISPATCH_FLOW - .iter() - .copied() - .filter(|requirement| { - requirement.role != FlowRole::TerminalBoundary || response_terminal_requested - }), - ); - } - if server_route_dispatch { - requirements.extend_from_slice(SERVER_ROUTE_DISPATCH_DETAIL_FLOW); - } - if !server_request_dispatch && !server_route_dispatch && client_request_dispatch { - if full_outbound_request { - push_full_client_outbound_request_flow(terms, &mut requirements); - } else { - requirements.extend_from_slice(CLIENT_REQUEST_DISPATCH_FLOW); - } - if packet_terms_have_any(terms, &["interceptor", "interceptors"]) { - requirements.push(REQUEST_INTERCEPTOR_REQUIREMENT); - } - } - if packet_terms_indicate_client_send_flow(terms) && !full_outbound_request { - push_client_send_requirements_for_terms(terms, &mut requirements); - } - if packet_terms_indicate_hook_cache_flow(terms) { - push_hook_cache_requirements_for_terms(terms, &mut requirements); - } - if packet_terms_indicate_event_loop_command_flow(terms) { - push_command_loop_requirements_for_terms(terms, &mut requirements); - } - if packet_terms_indicate_url_session_request_flow(terms) { - requirements.extend_from_slice(URL_SESSION_FLOW); - } - if packet_terms_indicate_sql_schema_flow(terms) { - requirements.extend_from_slice(SQL_SCHEMA_FLOW); - } - if packet_terms_indicate_html_css_template_structure_flow(terms) { - requirements.extend_from_slice(HTML_CSS_FLOW); - } - if packet_terms_indicate_stylesheet_animation_flow(terms) { - requirements.extend_from_slice(CSS_ANIMATION_FLOW); - } - if packet_terms_indicate_form_validation_flow(terms) { - requirements.extend_from_slice(FORM_VALIDATION_FLOW); - } - if packet_terms_indicate_shell_install_dispatch_flow(terms) { - requirements.extend_from_slice(SHELL_INSTALL_FLOW); - } - if packet_terms_indicate_buffered_io_flow(terms) { - requirements.extend_from_slice(BUFFERED_IO_FLOW); - } - if packet_terms_indicate_log_record_handler_flow(terms) { - requirements.extend_from_slice(LOG_HANDLER_FLOW); - } - if packet_terms_indicate_site_build_phase_flow(terms) { - requirements.extend_from_slice(SITE_BUILD_FLOW); - } - if packet_terms_indicate_mapper_configuration_plan_flow(terms) { - requirements.extend_from_slice(MAPPER_PLAN_FLOW); - } - if packet_terms_indicate_runtime_formatting_flow(terms) { - requirements.extend_from_slice(RUNTIME_FORMATTING_FLOW); - } - if packet_terms_indicate_string_predicate_flow(terms) { - push_string_predicate_requirements_for_terms(terms, &mut requirements); - } - if packet_terms_indicate_search_execution_flow(terms) { - push_search_execution_requirements_for_terms(terms, &mut requirements); - } - let search_evidence_requested = packet_terms_have_any(terms, &["search", "searches"]) - && packet_terms_have_any(terms, &["evidence", "proof", "provenance"]); - if search_evidence_requested { - let output_requested = packet_terms_have_any(terms, SEARCH_EVIDENCE_OUTPUT_ACTIONS) - || packet_terms_have_any(terms, &["surface", "handoff", "packet"]); - let classification_requested = - packet_terms_have_any(terms, SEARCH_EVIDENCE_CLASSIFICATION_ACTIONS) - || (output_requested - && packet_terms_have_any( - terms, - &["result", "results", "hit", "hits", "citation", "citations"], - )); - if classification_requested { - requirements.push(SEARCH_EVIDENCE_FLOW[0]); - } - if output_requested { - requirements.push(SEARCH_EVIDENCE_FLOW[1]); - } - } - dedupe_requirements(requirements) -} - -pub fn packet_flow_requirement_queries_for_terms( - terms: &[String], - task_class: PacketTaskClassDto, -) -> Vec { - let mut queries = Vec::new(); - for requirement in packet_flow_requirements_for_terms(terms, task_class) { - let _role = requirement.role; - let _requires_source = matches!( - requirement.coverage_mode, - CoverageMode::RequiresResolvedSourceOrGraph - | CoverageMode::AllowsSourceRange - | CoverageMode::AllowsLexicalSource - ); - for seed in requirement.query_seeds { - if !queries.iter().any(|query| query == seed) { - queries.push((*seed).to_string()); - } - } - } - queries -} - -/// Preserve the action phrasing next to each required flow step instead of replacing it with only -/// the generic role seed. A prompt such as "reads client input" carries substantially more symbol -/// identity than the seed "network input", while the seed still tells us which obligation owns -/// that clause. The query stays repository-neutral: it is assembled entirely from the prompt and -/// the declared requirement vocabulary. -pub fn packet_flow_requirement_context_queries_for_prompt( - question: &str, - terms: &[String], - task_class: PacketTaskClassDto, -) -> Vec<(&'static str, String)> { - let clauses = packet_prompt_flow_clauses(question); - let mut queries = Vec::new(); - for requirement in packet_flow_requirements_for_terms(terms, task_class) { - let seed_terms = requirement - .query_seeds - .iter() - .map(|seed| prompt_search_terms(seed)) - .filter(|seed| !seed.is_empty()) - .collect::>(); - let best = clauses - .iter() - .filter_map(|clause| { - let clause_terms = prompt_search_terms(clause) - .into_iter() - .filter(|term| !packet_flow_context_scaffolding(term)) - .collect::>(); - if clause_terms.len() < 2 { - return None; - } - let best_seed_match = seed_terms - .iter() - .map(|seed| { - let matched = seed - .iter() - .filter(|seed_term| clause_terms.iter().any(|term| term == *seed_term)) - .count(); - (matched, seed.len()) - }) - .filter(|(matched, _)| *matched > 0) - .max_by(|left, right| { - (left.0 * right.1) - .cmp(&(right.0 * left.1)) - .then_with(|| left.0.cmp(&right.0)) - })?; - let query_terms = packet_bounded_flow_context_terms(&clause_terms, &seed_terms); - let query = query_terms.join(" "); - let duplicates_seed = requirement.query_seeds.iter().any(|seed| { - prompt_search_terms(seed) - .join(" ") - .eq_ignore_ascii_case(&query) - }); - (!duplicates_seed && !query.is_empty()).then_some(( - best_seed_match.0, - best_seed_match.1, - query_terms.len(), - query, - )) - }) - .max_by(|left, right| { - (left.0 * right.1) - .cmp(&(right.0 * left.1)) - .then_with(|| left.0.cmp(&right.0)) - .then_with(|| right.2.cmp(&left.2)) - }); - if let Some((_, _, _, query)) = best { - queries.push((requirement.id, query)); - } - } - queries -} - -fn packet_prompt_flow_clauses(question: &str) -> Vec<&str> { - question - .split([',', ';', '.', '?', '!', '\n', '\r']) - .map(str::trim) - .filter(|clause| !clause.is_empty()) - .collect() -} - -fn packet_flow_context_scaffolding(term: &str) -> bool { - matches!( - term, - "answer" - | "cite" - | "cites" - | "describe" - | "explain" - | "file" - | "files" - | "name" - | "names" - | "source" - | "sources" - | "supporting" - | "symbol" - | "symbols" - | "trace" - ) -} - -fn packet_bounded_flow_context_terms( - clause_terms: &[String], - seed_terms: &[Vec], -) -> Vec { - const CONTEXT_TERM_LIMIT: usize = 6; - if clause_terms.len() <= CONTEXT_TERM_LIMIT { - return clause_terms.to_vec(); - } - let seed_term_set = seed_terms - .iter() - .flatten() - .map(String::as_str) - .collect::>(); - let matching_positions = clause_terms - .iter() - .enumerate() - .filter_map(|(index, term)| seed_term_set.contains(term.as_str()).then_some(index)) - .collect::>(); - let Some(center) = matching_positions.first().copied() else { - return clause_terms[..CONTEXT_TERM_LIMIT].to_vec(); - }; - let start = center - .saturating_sub(2) - .min(clause_terms.len().saturating_sub(CONTEXT_TERM_LIMIT)); - clause_terms[start..start + CONTEXT_TERM_LIMIT].to_vec() -} - -fn dedupe_requirements(requirements: Vec) -> Vec { - let mut deduped = Vec::new(); - for requirement in requirements { - if !deduped - .iter() - .any(|existing: &FlowRequirement| existing.id == requirement.id) - { - deduped.push(requirement); - } - } - deduped -} - -fn push_command_loop_requirements_for_terms( - terms: &[String], - requirements: &mut Vec, -) { - if packet_terms_indicate_command_server_bootstrap_flow(terms) { - requirements.push(COMMAND_SERVER_BOOTSTRAP_REQUIREMENT); - } - if packet_terms_indicate_command_event_loop_flow(terms) { - requirements.push(COMMAND_EVENT_LOOP_REQUIREMENT); - } - if packet_terms_indicate_network_command_input_flow(terms) { - requirements.push(COMMAND_NETWORK_INPUT_REQUIREMENT); - } - if packet_terms_indicate_command_dispatch_flow(terms) { - requirements.push(COMMAND_DISPATCH_REQUIREMENT); - } -} - -fn push_client_send_requirements_for_terms( - terms: &[String], - requirements: &mut Vec, -) { - let has_any = |needles: &[&str]| packet_terms_have_any(terms, needles); - if has_any(&[ - "top", "level", "public", "facade", "expose", "exposes", "api", "package", - ]) { - requirements.push(CLIENT_PUBLIC_FACADE_REQUIREMENT); - } - if has_any(&[ - "convenience", - "conveniences", - "method", - "methods", - "interface", - "interfaces", - "helper", - "helpers", - ]) && has_any(&["client", "clients", "http", "httpclient"]) - { - requirements.push(CLIENT_INTERFACE_HELPERS_REQUIREMENT); - } - if has_any(&[ - "finalize", - "finalizes", - "finalized", - "finalization", - "body", - "bodies", - "prepare", - "prepares", - "prepared", - ]) { - requirements.push(CLIENT_REQUEST_FINALIZATION_REQUIREMENT); - } - if has_any(&["send"]) - || (has_any(&["transport", "transports"]) && has_any(&["implementation", "implements"])) - { - requirements.push(CLIENT_TRANSPORT_SEND_REQUIREMENT); - } - if has_any(&[ - "response", - "responses", - "materialize", - "materializes", - "materialization", - "stream", - "boundary", - ]) { - requirements.push(CLIENT_RESPONSE_MATERIALIZATION_REQUIREMENT); - } - if requirements - .iter() - .all(|requirement| !requirement.id.starts_with("client_")) - { - requirements.push(CLIENT_TRANSPORT_SEND_REQUIREMENT); - } -} - -fn push_full_client_outbound_request_flow( - terms: &[String], - requirements: &mut Vec, -) { - let has_any = |needles: &[&str]| packet_terms_have_any(terms, needles); - requirements.push(CLIENT_PUBLIC_FACADE_REQUIREMENT); - if has_any(&[ - "convenience", - "conveniences", - "method", - "methods", - "interface", - "interfaces", - "helper", - "helpers", - ]) { - requirements.push(CLIENT_INTERFACE_HELPERS_REQUIREMENT); - } - requirements.push(CLIENT_REQUEST_DISPATCH_FLOW[0]); - requirements.push(CLIENT_REQUEST_FINALIZATION_REQUIREMENT); - requirements.push(CLIENT_REQUEST_DISPATCH_FLOW[1]); - requirements.push(CLIENT_REQUEST_DISPATCH_FLOW[2]); - requirements.push(CLIENT_TRANSPORT_SEND_REQUIREMENT); - if has_any(&[ - "response", - "responses", - "materialize", - "materializes", - "materialization", - "stream", - "boundary", - ]) { - requirements.push(CLIENT_RESPONSE_MATERIALIZATION_REQUIREMENT); - } -} - -fn push_hook_cache_requirements_for_terms( - terms: &[String], - requirements: &mut Vec, -) { - let has_any = |needles: &[&str]| packet_terms_have_any(terms, needles); - requirements.push(HOOK_PUBLIC_EXPORT_REQUIREMENT); - if has_any(&["serialize", "serializes", "serialized", "key", "keys"]) { - requirements.push(HOOK_KEY_SERIALIZATION_REQUIREMENT); - } - if has_any(&["cache", "caches", "caching", "helper", "helpers"]) { - requirements.push(HOOK_CACHE_HELPER_REQUIREMENT); - } - if has_any(&["mutate", "mutates", "mutation", "mutations"]) { - requirements.push(HOOK_MUTATION_FLOW_REQUIREMENT); - } -} - -const INDEXING_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "indexing_entrypoint", - role: FlowRole::Entrypoint, - query_seeds: &["indexing entrypoint"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_indexing, - roles: &[ - PacketEvidenceRole::IndexingWorkQueue, - PacketEvidenceRole::CommandEntrypoint, - PacketEvidenceRole::RuntimeOrchestration, - ], - }, - }, - FlowRequirement { - id: "indexing_storage", - role: FlowRole::StateOrStorage, - query_seeds: &["file discovery", "symbol extraction", "storage persistence"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_indexing, - roles: &[ - PacketEvidenceRole::PersistenceAndSearchProjection, - PacketEvidenceRole::SymbolExtraction, - PacketEvidenceRole::SnapshotRefresh, - PacketEvidenceRole::WorkspaceDiscoveryAndPlanning, - PacketEvidenceRole::CandidateFileConstruction, - ], - }, - }, -]; - -const SERVER_REQUEST_DISPATCH_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "request_entrypoint", - role: FlowRole::Registration, - query_seeds: &["application use", "route registration"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[ - PacketEvidenceRole::RouteHandling, - PacketEvidenceRole::AppServerRequestProtocol, - ], - carrier: citation_owns_server_request_entrypoint, - call_target: Some(server_request_entrypoint_call_target), - }, - }, - FlowRequirement { - id: "request_dispatch", - role: FlowRole::Dispatch, - query_seeds: &["application handle", "request dispatch"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[ - PacketEvidenceRole::RequestDispatch, - PacketEvidenceRole::CommandDispatch, - PacketEvidenceRole::RuntimeOrchestration, - ], - carrier: citation_owns_server_request_dispatch, - call_target: Some(server_request_dispatch_call_target), - }, - }, - FlowRequirement { - id: "request_terminal", - role: FlowRole::TerminalBoundary, - query_seeds: &["response send", "response finalization"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_request_terminal, - roles: &[ - PacketEvidenceRole::TransportAdapter, - PacketEvidenceRole::EventOutputProcessing, - PacketEvidenceRole::BufferedIo, - ], - carrier: citation_owns_server_response_terminal, - call_target: Some(server_response_terminal_call_target), - }, - }, -]; - -/// The ordered structural links inside an inbound route-dispatch flow. These sit beside the -/// established registration/dispatch/terminal requirements: they narrow broad route questions to -/// the handoffs needed to explain how a registered method reaches storage, how a server request -/// enters the dispatcher, and how a match reaches the handler chain. -const SERVER_ROUTE_DISPATCH_DETAIL_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "server_route_insertion_handoff", - role: FlowRole::Dispatch, - query_seeds: &["route registration insertion", "router route storage"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[], - carrier: citation_owns_server_route_registration, - call_target: Some(server_route_insertion_call_target), - }, - }, - FlowRequirement { - id: "server_request_handler_handoff", - role: FlowRole::Entrypoint, - query_seeds: &["server request handler", "serve http request dispatch"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[], - carrier: citation_owns_server_request_handler_entrypoint, - call_target: Some(server_request_dispatch_call_target), - }, - }, - FlowRequirement { - id: "server_route_match_lookup", - role: FlowRole::TransformOrValidate, - query_seeds: &["route tree lookup", "find matched route"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[], - carrier: citation_owns_server_route_match_dispatch, - call_target: Some(server_route_lookup_call_target), - }, - }, - FlowRequirement { - id: "server_handler_chain_handoff", - role: FlowRole::TerminalBoundary, - query_seeds: &["matched handlers context chain", "handler chain next"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_server_request, - roles: &[], - carrier: citation_owns_server_route_match_dispatch, - call_target: Some(server_handler_chain_call_target), - }, - }, -]; - -const CLIENT_REQUEST_DISPATCH_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "request_entrypoint", - role: FlowRole::Entrypoint, - query_seeds: &["default instance", "request method", "request entrypoint"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_client_request, - roles: &[ - PacketEvidenceRole::ClientFactory, - PacketEvidenceRole::CommandEntrypoint, - ], - carrier: citation_owns_client_request_entrypoint, - call_target: Some(client_request_entrypoint_call_target), - }, - }, - FlowRequirement { - id: "request_dispatch", - role: FlowRole::Dispatch, - query_seeds: &["request dispatch", "adapters", "transport adapter"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrOrderedCallBoundary { - subsystem: flow_belongs_to_client_request, - roles: &[PacketEvidenceRole::RequestDispatch], - carrier: citation_owns_client_request_dispatch, - incoming_source: client_request_dispatch_predecessor_call_source, - outgoing_target: client_request_dispatch_successor_call_target, - }, - }, - FlowRequirement { - id: "request_terminal", - role: FlowRole::Dispatch, - query_seeds: &["session adapter selection", "get adapter"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_client_adapter_selection), - }, -]; - -const REQUEST_INTERCEPTOR_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "request_interceptor_management", - role: FlowRole::Dispatch, - query_seeds: &["interceptor handlers", "request interceptor"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(packet_citation_owns_interceptor_management), -}; - -const URL_SESSION_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "client_request_entry", - role: FlowRole::Entrypoint, - query_seeds: &["session request creation", "request task resume"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_url_session, - roles: &[ - PacketEvidenceRole::ClientFactory, - PacketEvidenceRole::AppServerRequestProtocol, - PacketEvidenceRole::CommandEntrypoint, - ], - }, - }, - FlowRequirement { - id: "session_callbacks", - role: FlowRole::Dispatch, - query_seeds: &["session delegate callbacks", "data request validation"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_url_session, - roles: &[ - PacketEvidenceRole::RequestDispatch, - PacketEvidenceRole::EventLoop, - PacketEvidenceRole::RouteHandling, - PacketEvidenceRole::TransportAdapter, - ], - }, - }, -]; - -const CLIENT_PUBLIC_FACADE_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "client_public_facade", - role: FlowRole::Entrypoint, - query_seeds: &["http public get request", "public client facade"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_public_client_factory, - roles: &[PacketEvidenceRole::ClientFactory], - carrier: citation_owns_client_public_facade_helper, - call_target: Some(client_public_facade_successor_call_target), - }, -}; - -fn flow_belongs_to_public_client_factory(citation: &AgentCitationDto) -> bool { - if citation.kind != NodeKind::FUNCTION || !flow_belongs_to_client_request(citation) { - return false; - } - let terminal = citation - .display_name - .rsplit(['.', ':', '#']) - .find(|segment| !segment.is_empty()) - .unwrap_or(citation.display_name.as_str()); - !terminal.starts_with('_') -} - -const CLIENT_INTERFACE_HELPERS_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "client_interface_helpers", - role: FlowRole::Entrypoint, - query_seeds: &[ - "client convenience method", - "client interface helper", - "client type declaration", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_client_request_method), -}; - -const CLIENT_REQUEST_FINALIZATION_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "client_request_finalization", - role: FlowRole::TransformOrValidate, - query_seeds: &["request finalization", "transport-ready request object"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_client_request_finalization), -}; - -const CLIENT_TRANSPORT_SEND_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "client_transport_send", - role: FlowRole::TerminalBoundary, - query_seeds: &["transport send", "client send implementation"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_client_transport_send), -}; - -const CLIENT_RESPONSE_MATERIALIZATION_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "client_response_materialization", - role: FlowRole::TerminalBoundary, - query_seeds: &[ - "request response", - "response stream boundary", - "response class", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_client_response_materialization), -}; - -const HOOK_PUBLIC_EXPORT_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "hook_public_export", - role: FlowRole::Entrypoint, - query_seeds: &["public hook export", "hook argument wrapper"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_hook_public_export), -}; - -const HOOK_KEY_SERIALIZATION_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "hook_key_serialization", - role: FlowRole::TransformOrValidate, - query_seeds: &["key serialization", "serialize hook key"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_hook_key_serialization), -}; - -const HOOK_CACHE_HELPER_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "hook_cache_helper", - role: FlowRole::StateOrStorage, - query_seeds: &["cache helper", "cache state helper"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_hook_cache_helper), -}; - -const HOOK_MUTATION_FLOW_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "hook_mutation_flow", - role: FlowRole::Dispatch, - query_seeds: &["mutation helper", "mutate dispatch"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_hook_mutation_flow), -}; - -const COMMAND_SERVER_BOOTSTRAP_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "command_server_bootstrap", - role: FlowRole::Entrypoint, - query_seeds: &["server bootstrap", "command server entrypoint"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_command_server, - roles: &[ - PacketEvidenceRole::CommandEntrypoint, - PacketEvidenceRole::RuntimeOrchestration, - ], - }, -}; - -const COMMAND_EVENT_LOOP_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "command_event_loop", - role: FlowRole::Dispatch, - query_seeds: &[ - "event loop", - "event loop driver", - "process events callbacks", - "main event loop process events", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_command_server, - roles: &[], - carrier: citation_owns_command_event_loop_driver, - call_target: Some(command_event_loop_driver_call_target), - }, -}; - -const COMMAND_NETWORK_INPUT_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "command_network_input", - role: FlowRole::Dispatch, - query_seeds: &["network input", "network command input"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_network_input, - roles: &[PacketEvidenceRole::NetworkCommandInput], - }, -}; - -const COMMAND_DISPATCH_REQUIREMENT: FlowRequirement = FlowRequirement { - id: "command_dispatch", - role: FlowRole::Dispatch, - query_seeds: &[ - "command dispatch", - "command table dispatch", - "command routing checks", - "process command routing checks", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRolesOrCallBoundary { - subsystem: flow_belongs_to_command_server, - roles: &[], - carrier: citation_owns_command_router, - call_target: Some(command_router_call_target), - }, -}; - -const SQL_SCHEMA_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "sql_tables", - role: FlowRole::StateOrStorage, - query_seeds: &["sql table definitions", "CREATE TABLE"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_sql_schema, - roles: &[PacketEvidenceRole::SqlTableDefinition], - }, - }, - FlowRequirement { - id: "sql_relationships", - role: FlowRole::Configuration, - query_seeds: &["referential relationships", "schema constraints"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_sql_schema, - roles: &[PacketEvidenceRole::SqlRelationshipConstraint], - }, - }, -]; - -const HTML_CSS_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "html_app_shell", - role: FlowRole::Entrypoint, - query_seeds: &["html app shell", "module script entry"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_html_app_shell), - }, - FlowRequirement { - id: "css_structure", - role: FlowRole::Configuration, - query_seeds: &[ - "css theme defaults", - "css layout selectors", - "interactive element styles", - ], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_css_structure), - }, -]; - -const CSS_ANIMATION_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "css_animation_entrypoint", - role: FlowRole::Entrypoint, - query_seeds: &["animation stylesheet entrypoint", "css animation imports"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Atoms(&CSS_ANIMATION_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_css_animation_entrypoint), - }, - FlowRequirement { - id: "css_animation_structure", - role: FlowRole::Configuration, - query_seeds: &[ - "css animation variables", - "css animation base class", - "css animation keyframes", - ], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Atoms(&CSS_ANIMATION_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_css_animation_structure), - }, -]; - -const FORM_VALIDATION_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "form_native_constraints", - role: FlowRole::TransformOrValidate, - query_seeds: &[ - "native form constraints", - "constraint validation", - "validity state", - ], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_form_native_constraint), - }, - FlowRequirement { - id: "form_custom_validation", - role: FlowRole::TransformOrValidate, - query_seeds: &["custom validation", "custom error rendering"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_form_custom_validation), - }, - FlowRequirement { - id: "form_submit_guard", - role: FlowRole::TerminalBoundary, - query_seeds: &["submit prevent default", "submit invalid guard"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_form_submit_guard), - }, -]; - -const SHELL_INSTALL_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "shell_installer_bootstrap", - role: FlowRole::Entrypoint, - query_seeds: &["shell installer bootstrap", "install download helpers"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_shell_installer_bootstrap), - }, - FlowRequirement { - id: "shell_function_dispatch", - role: FlowRole::Dispatch, - query_seeds: &["shell function dispatch", "conditional version use"], - coverage_mode: CoverageMode::AllowsLexicalSource, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_shell_function_dispatch), - }, - FlowRequirement { - id: "shell_completion", - role: FlowRole::TerminalBoundary, - query_seeds: &["shell completion"], - coverage_mode: CoverageMode::DiagnosticOnly, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_shell_completion), - }, -]; - -const BUFFERED_IO_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "buffered_storage", - role: FlowRole::StateOrStorage, - query_seeds: &["buffer storage", "source sink buffer"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_buffer_storage), - }, - FlowRequirement { - id: "buffered_read_write", - role: FlowRole::Dispatch, - query_seeds: &["source read buffer", "sink write buffer"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_buffer_read_write), - }, -]; - -const LOG_HANDLER_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "logger_event", - role: FlowRole::Entrypoint, - query_seeds: &["logger record", "record creation"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Atoms(&LOG_HANDLER_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_log_record_creation), - }, - FlowRequirement { - id: "handler_processing", - role: FlowRole::Dispatch, - query_seeds: &[ - "handler registration", - "handler processing", - "handler interface", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Atoms(&LOG_HANDLER_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_log_handler_processing), - }, -]; - -const SITE_BUILD_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "site_lifecycle", - role: FlowRole::Entrypoint, - query_seeds: &[ - "site build lifecycle", - "Site.process reset read generate render cleanup write", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_site_lifecycle), - }, - FlowRequirement { - id: "site_reader", - role: FlowRole::StateOrStorage, - query_seeds: &[ - "Reader.read site content layouts collections pages data", - "static site content reader", - ], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_site_reader), - }, - FlowRequirement { - id: "site_terminal", - role: FlowRole::TerminalBoundary, - query_seeds: &["read generate render write", "renderer render"], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_site_terminal), - }, -]; - -const MAPPER_PLAN_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "mapper_config", - role: FlowRole::Configuration, - query_seeds: &[ - "mapper runtime api", - "mapper interface", - "mapping configuration", - "type map plan", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Atoms(&MAPPER_PLAN_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_mapper_configuration), - }, - FlowRequirement { - id: "mapper_execution", - role: FlowRole::Dispatch, - query_seeds: &["mapping execution plan", "source destination mapping"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Atoms(&MAPPER_PLAN_FLOW_PROOF), - evidence: EvidencePredicate::CitedCarrier(citation_owns_mapper_execution), - }, -]; - -const RUNTIME_FORMATTING_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "format_arguments", - role: FlowRole::TransformOrValidate, - query_seeds: &[ - "format arguments", - "format output", - "type erased argument store", - "dynamic argument collection", - "stored format arguments", - ], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_format_arguments), - }, - FlowRequirement { - id: "formatter_fallback", - role: FlowRole::ErrorOrFallback, - query_seeds: &[ - "formatting failure", - "formatter fallback", - "formatter exception type", - ], - coverage_mode: CoverageMode::AllowsSourceRange, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_formatter_fallback), - }, -]; - -const STRING_PREDICATE_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "string_blank_predicate", - role: FlowRole::TransformOrValidate, - query_seeds: &["string blank predicate", "whitespace predicate"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_string_blank_predicate), - }, - FlowRequirement { - id: "string_empty_predicate", - role: FlowRole::TransformOrValidate, - query_seeds: &["string empty predicate"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_string_empty_predicate), - }, - FlowRequirement { - id: "string_region_handoff", - role: FlowRole::Dispatch, - query_seeds: &["string region match", "case-sensitive string comparison"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_string_region_handoff), - }, -]; - -fn push_string_predicate_requirements_for_terms( - terms: &[String], - requirements: &mut Vec, -) { - let generic = packet_terms_have_any(terms, &["predicate", "predicates"]); - if generic || packet_terms_have_any(terms, &["blank", "whitespace", "trim", "trims"]) { - requirements.push(STRING_PREDICATE_FLOW[0]); - } - if generic || packet_terms_have_any(terms, &["empty"]) { - requirements.push(STRING_PREDICATE_FLOW[1]); - } - if generic - || packet_terms_have_any( - terms, - &[ - "case", - "cases", - "sensitive", - "region", - "regions", - "match", - "matches", - "matching", - ], - ) - { - requirements.push(STRING_PREDICATE_FLOW[2]); - } -} - -const SEARCH_EXECUTION_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "search_entrypoint", - role: FlowRole::Entrypoint, - query_seeds: &["main flags parse run"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_search, - roles: &[PacketEvidenceRole::CommandEntrypoint], - }, - }, - FlowRequirement { - id: "search_argument_planning", - role: FlowRole::Configuration, - query_seeds: &["flag parsing"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_argument_planning), - }, - FlowRequirement { - id: "search_driver", - role: FlowRole::Dispatch, - query_seeds: &["parallel search walker"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_search, - roles: &[PacketEvidenceRole::SearchDriver], - }, - }, - FlowRequirement { - id: "search_candidate_traversal", - role: FlowRole::Dispatch, - query_seeds: &["parallel search walk builder"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_candidate_traversal), - }, - FlowRequirement { - id: "search_haystack_construction", - role: FlowRole::TransformOrValidate, - query_seeds: &["haystack builder"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_haystack_construction), - }, - FlowRequirement { - id: "search_matcher_setup", - role: FlowRole::Configuration, - query_seeds: &["argument matcher"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_matcher_setup), - }, - FlowRequirement { - id: "search_searcher_setup", - role: FlowRole::Configuration, - query_seeds: &["searcher construction"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_searcher_setup), - }, - FlowRequirement { - id: "search_printer_setup", - role: FlowRole::Configuration, - query_seeds: &["result printer construction"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_printer_setup), - }, - FlowRequirement { - id: "search_worker_construction", - role: FlowRole::Configuration, - query_seeds: &["search worker matcher searcher printer"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_worker_construction), - }, - FlowRequirement { - id: "search_dispatch", - role: FlowRole::Dispatch, - query_seeds: &["search execution"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedRoles { - subsystem: flow_belongs_to_search, - roles: &[PacketEvidenceRole::SearchExecutionUnit], - }, - }, -]; - -fn push_search_execution_requirements_for_terms( - terms: &[String], - requirements: &mut Vec, -) { - requirements.push(SEARCH_EXECUTION_FLOW[0]); - if packet_terms_have_any( - terms, - &[ - "arg", - "args", - "argument", - "arguments", - "argv", - "flag", - "flags", - "option", - "options", - ], - ) { - requirements.push(SEARCH_EXECUTION_FLOW[1]); - } - let detailed_driver = packet_terms_have_any( - terms, - &[ - "candidate", - "candidates", - "haystack", - "haystacks", - "matcher", - "matchers", - "printer", - "printers", - "searcher", - "searchers", - "walk", - "walker", - "walkers", - "walks", - ], - ); - if detailed_driver { - requirements.push(SEARCH_EXECUTION_FLOW[2]); - } - if packet_terms_have_any( - terms, - &[ - "candidate", - "candidates", - "walk", - "walker", - "walkers", - "walks", - ], - ) { - requirements.push(SEARCH_EXECUTION_FLOW[3]); - } - if packet_terms_have_any(terms, &["haystack", "haystacks"]) { - requirements.push(SEARCH_EXECUTION_FLOW[4]); - } - if packet_terms_have_any(terms, &["matcher", "matchers"]) { - requirements.push(SEARCH_EXECUTION_FLOW[5]); - } - if packet_terms_have_any(terms, &["searcher", "searchers"]) { - requirements.push(SEARCH_EXECUTION_FLOW[6]); - } - if packet_terms_have_any(terms, &["printer", "printers"]) { - requirements.push(SEARCH_EXECUTION_FLOW[7]); - } - let worker_construction = packet_terms_have_any(terms, &["matcher", "matchers"]) - && packet_terms_have_any(terms, &["searcher", "searchers"]) - && packet_terms_have_any(terms, &["printer", "printers"]); - if worker_construction { - requirements.push(SEARCH_EXECUTION_FLOW[8]); - } - requirements.push(SEARCH_EXECUTION_FLOW[9]); -} - -const SEARCH_EVIDENCE_FLOW: &[FlowRequirement] = &[ - FlowRequirement { - id: "search_evidence_classification", - role: FlowRole::TransformOrValidate, - query_seeds: &["search result evidence classification", "evidence tier"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_evidence_classification), - }, - FlowRequirement { - id: "search_evidence_output", - role: FlowRole::TerminalBoundary, - query_seeds: &["search result evidence output", "packet evidence handoff"], - coverage_mode: CoverageMode::RequiresResolvedSourceOrGraph, - proof: FlowProofSpec::Legacy, - evidence: EvidencePredicate::CitedCarrier(citation_owns_search_evidence_output), - }, -]; - -/// Every requirement table, grouped the way a single question raises them. Requirements that share -/// a group and a `FlowRole` are the ones that must stay separable by evidence, so tests need the -/// grouping and not just a flat list. -#[cfg(test)] -pub fn all_flow_requirement_groups() -> Vec<(&'static str, Vec)> { - let mut client_dispatch = CLIENT_REQUEST_DISPATCH_FLOW.to_vec(); - client_dispatch.push(REQUEST_INTERCEPTOR_REQUIREMENT); - vec![ - ("indexing", INDEXING_FLOW.to_vec()), - ( - "server_request_dispatch", - SERVER_REQUEST_DISPATCH_FLOW.to_vec(), - ), - ( - "server_route_dispatch_detail", - SERVER_ROUTE_DISPATCH_DETAIL_FLOW.to_vec(), - ), - ("client_request_dispatch", client_dispatch), - ("url_session", URL_SESSION_FLOW.to_vec()), - ( - "client_send", - vec![ - CLIENT_PUBLIC_FACADE_REQUIREMENT, - CLIENT_INTERFACE_HELPERS_REQUIREMENT, - CLIENT_REQUEST_FINALIZATION_REQUIREMENT, - CLIENT_TRANSPORT_SEND_REQUIREMENT, - CLIENT_RESPONSE_MATERIALIZATION_REQUIREMENT, - ], - ), - ( - "hook_cache", - vec![ - HOOK_PUBLIC_EXPORT_REQUIREMENT, - HOOK_KEY_SERIALIZATION_REQUIREMENT, - HOOK_CACHE_HELPER_REQUIREMENT, - HOOK_MUTATION_FLOW_REQUIREMENT, - ], - ), - ( - "command_loop", - vec![ - COMMAND_SERVER_BOOTSTRAP_REQUIREMENT, - COMMAND_EVENT_LOOP_REQUIREMENT, - COMMAND_NETWORK_INPUT_REQUIREMENT, - COMMAND_DISPATCH_REQUIREMENT, - ], - ), - ("sql_schema", SQL_SCHEMA_FLOW.to_vec()), - ("html_css", HTML_CSS_FLOW.to_vec()), - ("css_animation", CSS_ANIMATION_FLOW.to_vec()), - ("form_validation", FORM_VALIDATION_FLOW.to_vec()), - ("shell_install", SHELL_INSTALL_FLOW.to_vec()), - ("buffered_io", BUFFERED_IO_FLOW.to_vec()), - ("log_handler", LOG_HANDLER_FLOW.to_vec()), - ("site_build", SITE_BUILD_FLOW.to_vec()), - ("mapper_plan", MAPPER_PLAN_FLOW.to_vec()), - ("runtime_formatting", RUNTIME_FORMATTING_FLOW.to_vec()), - ("string_predicates", STRING_PREDICATE_FLOW.to_vec()), - ("search_execution", SEARCH_EXECUTION_FLOW.to_vec()), - ("search_evidence", SEARCH_EVIDENCE_FLOW.to_vec()), - ] -} - -#[cfg(test)] -pub fn all_flow_requirements() -> Vec { - all_flow_requirement_groups() - .into_iter() - .flat_map(|(_, requirements)| requirements) - .collect() -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::packet_evidence_carriers::carrier_taxonomy_vocabulary; - use crate::packet_proof_atoms::FlowProofFormula; - use crate::packet_terms::packet_probe_terms; - use codestory_contracts::api::{EdgeId, NodeId, NodeKind, SearchHitOrigin}; - use std::collections::BTreeMap; - - /// Contract rev 5 scope table: exactly six requirement ids carry real - /// proof formulas, each referencing its stage-1 const formula group; - /// everything else — all `SITE_BUILD_FLOW` ids explicitly included — is - /// `FlowProofSpec::Legacy`. - #[test] - fn only_the_six_shard_requirements_carry_atom_proof_formulas() { - let expected: BTreeMap<&str, &'static FlowProofFormula> = [ - ("logger_event", &LOG_HANDLER_FLOW_PROOF), - ("handler_processing", &LOG_HANDLER_FLOW_PROOF), - ("mapper_config", &MAPPER_PLAN_FLOW_PROOF), - ("mapper_execution", &MAPPER_PLAN_FLOW_PROOF), - ("css_animation_entrypoint", &CSS_ANIMATION_FLOW_PROOF), - ("css_animation_structure", &CSS_ANIMATION_FLOW_PROOF), - ] - .into_iter() - .collect(); - let mut formula_bearing_ids = std::collections::BTreeSet::new(); - for requirement in all_flow_requirements() { - match requirement.proof.formula() { - Some(formula) => { - formula_bearing_ids.insert(requirement.id); - let expected_formula = expected.get(requirement.id).unwrap_or_else(|| { - panic!( - "requirement `{}` carries a proof formula but is not one of the six \ - shard ids the contract scope table names", - requirement.id - ) - }); - // `&CONST` promotion does not guarantee one address per - // const, so identity is compared structurally: atom ids - // are unique across the three formula groups. - assert_eq!( - formula.requirements(), - expected_formula.requirements(), - "requirement `{}` references the wrong const formula group", - requirement.id - ); - assert_eq!( - formula.atoms_for(requirement.id), - expected_formula.atoms_for(requirement.id), - "requirement `{}` references the wrong const formula group", - requirement.id - ); - assert!( - formula.requirements().contains(&requirement.id), - "requirement `{}` references a formula that names no atom for it", - requirement.id - ); - assert!( - !formula.atoms_for(requirement.id).is_empty(), - "requirement `{}` has no materially required atoms in its formula", - requirement.id - ); - } - None => { - assert!( - !expected.contains_key(requirement.id), - "shard requirement `{}` lost its proof formula", - requirement.id - ); - } - } - } - assert_eq!( - formula_bearing_ids.iter().copied().collect::>(), - expected.keys().copied().collect::>(), - "the formula-bearing requirement ids must be exactly the contract's six" - ); - for requirement in SITE_BUILD_FLOW { - assert!( - requirement.proof.formula().is_none(), - "SITE_BUILD_FLOW id `{}` must stay FlowProofSpec::Legacy (contract scope table)", - requirement.id - ); - } - } - - fn client_requirement_ids(prompt: &str) -> Vec<&'static str> { - packet_flow_requirements_for_terms( - &packet_probe_terms(prompt), - PacketTaskClassDto::DataFlow, - ) - .into_iter() - .filter_map(|requirement| { - requirement - .id - .starts_with("client_") - .then_some(requirement.id) - }) - .collect() - } - - #[test] - fn flow_queries_preserve_prompt_actions_near_each_required_step() { - let prompt = "Trace how Redis initializes the server, enters the event loop, reads client input, and routes a command for execution. Cite the source files and name the supporting symbols."; - let terms = packet_probe_terms(prompt); - let queries = packet_flow_requirement_context_queries_for_prompt( - prompt, - &terms, - PacketTaskClassDto::RouteTracing, - ) - .into_iter() - .collect::>(); - - assert_eq!( - queries.get("command_server_bootstrap").map(String::as_str), - Some("redis initializes server") - ); - assert_eq!( - queries.get("command_event_loop").map(String::as_str), - Some("enters event loop") - ); - assert_eq!( - queries.get("command_network_input").map(String::as_str), - Some("reads client input") - ); - assert_eq!( - queries.get("command_dispatch").map(String::as_str), - Some("routes command execution") - ); - } - - #[test] - fn broad_client_send_prompt_requires_full_lifecycle() { - assert_eq!( - client_requirement_ids( - "Explain how an HTTP client exposes top-level helpers, provides client convenience methods, finalizes requests before transport send, and materializes responses." - ), - vec![ - "client_public_facade", - "client_interface_helpers", - "client_request_finalization", - "client_transport_send", - "client_response_materialization", - ] - ); - } - - #[test] - fn inflected_outbound_request_plans_one_ordered_six_stage_lifecycle() { - for action in ["send", "sends", "sending", "sent"] { - let prompt = format!( - "Explain how a top-level request call becomes a prepared request and {action} it through a session adapter." - ); - let terms = packet_probe_terms(&prompt); - let requirements = - packet_flow_requirements_for_terms(&terms, PacketTaskClassDto::DataFlow); - assert_eq!( - requirements - .iter() - .map(|requirement| requirement.id) - .collect::>(), - [ - "client_public_facade", - "request_entrypoint", - "client_request_finalization", - "request_dispatch", - "request_terminal", - "client_transport_send", - ], - "{action}: {terms:?}" - ); - - let queries = - packet_flow_requirement_queries_for_terms(&terms, PacketTaskClassDto::DataFlow); - assert_eq!( - queries - .iter() - .collect::>() - .len(), - queries.len(), - "the lifecycle must not dispatch duplicate queries: {queries:?}" - ); - } - } - - #[test] - fn focused_client_finalization_prompt_does_not_require_full_lifecycle() { - assert_eq!( - client_requirement_ids( - "Explain how an HTTP client finalizes requests before transport." - ), - vec!["client_request_finalization"] - ); - } - - #[test] - fn focused_client_transport_prompt_does_not_require_full_lifecycle() { - assert_eq!( - client_requirement_ids("Explain how an HTTP client performs transport send."), - vec!["client_transport_send"] - ); - } - - #[test] - fn runtime_formatting_queries_include_argument_store_and_exception_seeds() { - let queries = packet_flow_requirement_queries_for_terms( - &packet_probe_terms( - "Explain how formatting arguments become type-erased format args and reach vformat output.", - ), - PacketTaskClassDto::ArchitectureExplanation, - ); - for expected in [ - "format arguments", - "type erased argument store", - "dynamic argument collection", - "stored format arguments", - "formatter exception type", - ] { - assert!( - queries.iter().any(|query| query == expected), - "expected {expected:?} in {queries:?}" - ); - } - } - - #[test] - fn search_evidence_handoff_adds_classification_and_output_obligations() { - let full_flow = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Explain how search results are ranked and turned into the final evidence packet.", - ), - PacketTaskClassDto::DataFlow, - ); - let full_flow_ids = full_flow - .iter() - .map(|requirement| requirement.id) - .collect::>(); - - assert!(full_flow_ids.contains(&"search_entrypoint")); - assert!(full_flow_ids.contains(&"search_dispatch")); - assert!(full_flow_ids.contains(&"search_evidence_classification")); - assert!(full_flow_ids.contains(&"search_evidence_output")); - - let focused = packet_flow_requirements_for_terms( - &packet_probe_terms("Explain search result evidence classification and output."), - PacketTaskClassDto::ArchitectureExplanation, - ); - let focused_ids = focused - .iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!( - focused_ids, - ["search_evidence_classification", "search_evidence_output"] - ); - - let verb_ids = packet_flow_requirements_for_terms( - &packet_probe_terms("How does search classify evidence and emit it?"), - PacketTaskClassDto::DataFlow, - ) - .into_iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!( - verb_ids, - ["search_evidence_classification", "search_evidence_output"] - ); - - let classification_only = packet_flow_requirements_for_terms( - &packet_probe_terms("How is the search evidence tier assigned?"), - PacketTaskClassDto::ArchitectureExplanation, - ) - .into_iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!(classification_only, ["search_evidence_classification"]); - - let output_only = packet_flow_requirements_for_terms( - &packet_probe_terms("How does search emit evidence?"), - PacketTaskClassDto::ArchitectureExplanation, - ) - .into_iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!(output_only, ["search_evidence_output"]); - - let surface_output = packet_flow_requirements_for_terms( - &packet_probe_terms("How does search surface evidence?"), - PacketTaskClassDto::ArchitectureExplanation, - ) - .into_iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!(surface_output, ["search_evidence_output"]); - } - - #[test] - fn search_execution_flow_uses_one_separable_requirement_per_requested_stage() { - let prompt = "Explain how ripgrep parses CLI flags, walks candidate files, and executes a search over each haystack through matcher, searcher, and printer components."; - let requirements = packet_flow_requirements_for_terms( - &packet_probe_terms(prompt), - PacketTaskClassDto::ArchitectureExplanation, - ); - let ids = requirements - .iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!( - ids, - [ - "search_entrypoint", - "search_argument_planning", - "search_driver", - "search_candidate_traversal", - "search_haystack_construction", - "search_matcher_setup", - "search_searcher_setup", - "search_printer_setup", - "search_worker_construction", - "search_dispatch", - ] - ); - - let witnesses = [ - ( - "search_argument_planning", - witness( - "flags::parse", - "crates/core/flags/mod.rs", - NodeKind::FUNCTION, - ), - ), - ( - "search_candidate_traversal", - witness( - "HiArgs::walk_builder", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - "search_haystack_construction", - witness( - "HiArgs::haystack_builder", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - "search_matcher_setup", - witness( - "HiArgs::matcher", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - "search_searcher_setup", - witness( - "HiArgs::searcher", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - "search_printer_setup", - witness( - "HiArgs::printer", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - "search_worker_construction", - witness( - "HiArgs::search_worker", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ]; - let component_ids = [ - "search_argument_planning", - "search_candidate_traversal", - "search_haystack_construction", - "search_matcher_setup", - "search_searcher_setup", - "search_printer_setup", - "search_worker_construction", - ]; - for (expected_id, citation) in witnesses { - let proved = requirements - .iter() - .filter(|requirement| component_ids.contains(&requirement.id)) - .filter(|requirement| requirement.evidence.citation_proves(&citation)) - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!(proved, [expected_id], "{citation:#?}"); - } - - for unrelated in [ - witness("matcher", "src/image.rs", NodeKind::FUNCTION), - witness("haystack", "src/text.rs", NodeKind::FUNCTION), - witness("Formatter::printer", "src/output.rs", NodeKind::METHOD), - witness("Database::searcher", "src/storage.rs", NodeKind::METHOD), - witness("WalkBuilder::build", "src/files.rs", NodeKind::METHOD), - witness("SearchWorker::search", "src/search.rs", NodeKind::METHOD), - ] { - assert!( - requirements - .iter() - .filter(|requirement| component_ids.contains(&requirement.id)) - .all(|requirement| !requirement.evidence.citation_proves(&unrelated)), - "an unrelated component must not close a search-flow stage: {unrelated:#?}", - ); - } - - let generic_ids = packet_flow_requirements_for_terms( - &packet_probe_terms("Explain the search flow."), - PacketTaskClassDto::ArchitectureExplanation, - ) - .into_iter() - .map(|requirement| requirement.id) - .collect::>(); - assert_eq!(generic_ids, ["search_entrypoint", "search_dispatch"]); - - let queries = packet_flow_requirement_queries_for_terms( - &packet_probe_terms(prompt), - PacketTaskClassDto::ArchitectureExplanation, - ); - for expected in [ - "main flags parse run", - "flag parsing", - "parallel search walker", - "parallel search walk builder", - "haystack builder", - "argument matcher", - "searcher construction", - "result printer construction", - "search worker matcher searcher printer", - "search execution", - ] { - assert!( - queries.iter().any(|query| query == expected), - "{queries:#?}" - ); - } - } - - #[test] - fn client_request_flow_uses_behavior_owner_probes_without_server_registration() { - let requirements = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Explain how a default HTTP client instance is created, then a request passes through request and response interceptors before dispatch to the adapter and transport.", - ), - PacketTaskClassDto::ArchitectureExplanation, - ); - let entrypoint = requirements - .iter() - .find(|requirement| requirement.id == "request_entrypoint") - .expect("client request flow should require an entrypoint"); - let queries = requirements - .iter() - .flat_map(|requirement| requirement.query_seeds.iter().copied()) - .collect::>(); - - assert_eq!(entrypoint.role, FlowRole::Entrypoint); - for expected in [ - "default instance", - "request method", - "interceptor handlers", - "adapters", - ] { - assert!( - queries.contains(&expected), - "client request flow should probe {expected}" - ); - } - for server_only in [ - "application use", - "application handle", - "route registration", - ] { - assert!( - !queries.contains(&server_only), - "client request flow should not probe {server_only}" - ); - } - } - - #[test] - fn server_request_flow_leads_with_carrier_aligned_symbols() { - let requirements = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Trace how an HTTP server routes an incoming request through route registration, request handler dispatch, and response finalization.", - ), - PacketTaskClassDto::RouteTracing, - ); - let entrypoint = requirements - .iter() - .find(|requirement| requirement.id == "request_entrypoint") - .expect("server request flow should require an entrypoint"); - let queries = requirements - .iter() - .flat_map(|requirement| requirement.query_seeds.iter().copied()) - .collect::>(); - - assert_eq!(entrypoint.role, FlowRole::Registration); - for expected in [ - "application use", - "route registration", - "application handle", - "request dispatch", - "response send", - "response finalization", - ] { - assert!( - queries.contains(&expected), - "server request flow should probe {expected}" - ); - } - for client_only in [ - "default instance", - "request method", - "interceptor handlers", - "adapters", - "transport adapter", - ] { - assert!( - !queries.contains(&client_only), - "server request flow should not probe {client_only}" - ); - } - } - - #[test] - fn http_handle_owner_proves_server_request_dispatch() { - let requirement = - packet_flow_requirements_for_terms( - &packet_probe_terms( - "Trace how an HTTP server routes an incoming request through route registration, request handler dispatch, and response finalization.", - ), - PacketTaskClassDto::RouteTracing, - ) - .into_iter() - .find(|requirement| requirement.id == "request_dispatch") - .expect("server request flow should require dispatch"); - - assert!(requirement.evidence.citation_proves(&witness( - "ServerEngine.handleHTTPRequest", - "src/http/server.go", - NodeKind::METHOD, - ))); - assert!(requirement.evidence.citation_proves(&witness( - "app.handle", - "lib/application.js", - NodeKind::FUNCTION, - ))); - for negative in [ - "IRouter.Group", - "Telemetry.handleHTTPRequest", - "HttpClient.handleHTTPRequest", - "handleHTTPRequest", - ] { - assert!( - !requirement.evidence.citation_proves(&witness( - negative, - "src/http/server.go", - NodeKind::METHOD, - )), - "{negative} must not prove server request dispatch" - ); - } - } - - #[test] - fn named_client_send_proves_client_transport_send() { - let requirement = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Explain how an HTTP client exposes top-level helpers, provides client convenience methods, finalizes requests before transport send, and materializes responses.", - ), - PacketTaskClassDto::DataFlow, - ) - .into_iter() - .find(|requirement| requirement.id == "client_transport_send") - .expect("client send flow should require transport send"); - - for positive in ["BaseClient.send", "IOClient.send", "HttpTransport.send"] { - assert!( - requirement.evidence.citation_proves(&witness( - positive, - "src/client.rs", - NodeKind::METHOD, - )), - "{positive} should prove client transport send" - ); - } - for negative in ["Session.send", "DatabaseClient.send", "HookClient.send"] { - assert!( - !requirement.evidence.citation_proves(&witness( - negative, - "src/client.rs", - NodeKind::METHOD, - )), - "{negative} must not prove client transport send" - ); - } - } - - #[test] - fn server_route_flow_requires_registration_and_dispatch_without_response_terminal() { - let requirements = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Trace how an Express application registers middleware and routes, then dispatches an incoming request through router layers to a route handler.", - ), - PacketTaskClassDto::RouteTracing, - ); - let ids = requirements - .iter() - .map(|requirement| requirement.id) - .collect::>(); - let queries = requirements - .iter() - .flat_map(|requirement| requirement.query_seeds.iter().copied()) - .collect::>(); - - assert_eq!( - ids, - [ - "request_entrypoint", - "request_dispatch", - "server_route_insertion_handoff", - "server_request_handler_handoff", - "server_route_match_lookup", - "server_handler_chain_handoff", - ] - ); - for expected in [ - "application use", - "route registration", - "application handle", - "request dispatch", - "route registration insertion", - "router route storage", - "server request handler", - "serve http request dispatch", - "route tree lookup", - "find matched route", - "matched handlers context chain", - "handler chain next", - ] { - assert!( - queries.contains(&expected), - "server route flow should probe {expected}" - ); - } - for out_of_scope in [ - "response send", - "response finalization", - "transport send", - "transport adapter", - ] { - assert!( - !queries.contains(&out_of_scope), - "route-to-handler prompt should not require {out_of_scope}" - ); - } - } - - #[test] - fn server_route_flow_includes_response_terminal_when_explicitly_requested() { - let requirements = packet_flow_requirements_for_terms( - &packet_probe_terms( - "Trace how Express creates an application, registers middleware/routes, and handles an incoming request through the router and response helpers.", - ), - PacketTaskClassDto::RouteTracing, - ); - let ids = requirements - .iter() - .map(|requirement| requirement.id) - .collect::>(); - let queries = requirements - .iter() - .flat_map(|requirement| requirement.query_seeds.iter().copied()) - .collect::>(); - - assert_eq!( - ids, - [ - "request_entrypoint", - "request_dispatch", - "request_terminal", - "server_route_insertion_handoff", - "server_request_handler_handoff", - "server_route_match_lookup", - "server_handler_chain_handoff", - ] - ); - for expected in ["response send", "response finalization"] { - assert!( - queries.contains(&expected), - "response-helper route flow should probe {expected}" - ); - } - } - - /// The checked-in inventory of every requirement a question can raise, as - /// `id | role | coverage mode`. - /// - /// This exists so that a requirement can never be quietly removed to make a gate pass. The - /// previous invariant asked "can any evidence role carry this requirement's `FlowRole`?", which - /// a failing lane could satisfy by deleting the requirement; this one fails on removal too, and - /// the only way past it is to edit the list in the diff a reviewer reads. - const FLOW_REQUIREMENT_INVENTORY: &[&str] = &[ - "buffered_read_write | dispatch | RequiresResolvedSourceOrGraph", - "buffered_storage | state_or_storage | AllowsSourceRange", - "client_interface_helpers | entrypoint | RequiresResolvedSourceOrGraph", - "client_public_facade | entrypoint | RequiresResolvedSourceOrGraph", - "client_request_finalization | transform_or_validate | RequiresResolvedSourceOrGraph", - "client_response_materialization | terminal_boundary | RequiresResolvedSourceOrGraph", - "client_transport_send | terminal_boundary | RequiresResolvedSourceOrGraph", - "command_dispatch | dispatch | RequiresResolvedSourceOrGraph", - "command_event_loop | dispatch | RequiresResolvedSourceOrGraph", - "command_network_input | dispatch | RequiresResolvedSourceOrGraph", - "command_server_bootstrap | entrypoint | RequiresResolvedSourceOrGraph", - "css_animation_entrypoint | entrypoint | AllowsLexicalSource", - "css_animation_structure | configuration | AllowsLexicalSource", - "css_structure | configuration | AllowsLexicalSource", - "form_custom_validation | transform_or_validate | AllowsLexicalSource", - "form_native_constraints | transform_or_validate | AllowsLexicalSource", - "form_submit_guard | terminal_boundary | AllowsLexicalSource", - "format_arguments | transform_or_validate | RequiresResolvedSourceOrGraph", - "formatter_fallback | error_or_fallback | AllowsSourceRange", - "handler_processing | dispatch | RequiresResolvedSourceOrGraph", - "hook_cache_helper | state_or_storage | AllowsSourceRange", - "hook_key_serialization | transform_or_validate | AllowsSourceRange", - "hook_mutation_flow | dispatch | AllowsSourceRange", - "hook_public_export | entrypoint | AllowsSourceRange", - "html_app_shell | entrypoint | AllowsLexicalSource", - "indexing_entrypoint | entrypoint | RequiresResolvedSourceOrGraph", - "indexing_storage | state_or_storage | AllowsSourceRange", - "logger_event | entrypoint | RequiresResolvedSourceOrGraph", - "mapper_config | configuration | RequiresResolvedSourceOrGraph", - "mapper_execution | dispatch | RequiresResolvedSourceOrGraph", - "request_dispatch | dispatch | RequiresResolvedSourceOrGraph", - "request_entrypoint | entrypoint | RequiresResolvedSourceOrGraph", - "request_entrypoint | registration | RequiresResolvedSourceOrGraph", - "request_interceptor_management | dispatch | RequiresResolvedSourceOrGraph", - "request_terminal | dispatch | RequiresResolvedSourceOrGraph", - "request_terminal | terminal_boundary | RequiresResolvedSourceOrGraph", - "search_argument_planning | configuration | RequiresResolvedSourceOrGraph", - "search_candidate_traversal | dispatch | RequiresResolvedSourceOrGraph", - "search_dispatch | dispatch | RequiresResolvedSourceOrGraph", - "search_driver | dispatch | RequiresResolvedSourceOrGraph", - "search_evidence_classification | transform_or_validate | RequiresResolvedSourceOrGraph", - "search_evidence_output | terminal_boundary | RequiresResolvedSourceOrGraph", - "search_entrypoint | entrypoint | RequiresResolvedSourceOrGraph", - "search_haystack_construction | transform_or_validate | RequiresResolvedSourceOrGraph", - "search_matcher_setup | configuration | RequiresResolvedSourceOrGraph", - "search_printer_setup | configuration | RequiresResolvedSourceOrGraph", - "search_searcher_setup | configuration | RequiresResolvedSourceOrGraph", - "search_worker_construction | configuration | RequiresResolvedSourceOrGraph", - "server_handler_chain_handoff | terminal_boundary | RequiresResolvedSourceOrGraph", - "server_request_handler_handoff | entrypoint | RequiresResolvedSourceOrGraph", - "server_route_insertion_handoff | dispatch | RequiresResolvedSourceOrGraph", - "server_route_match_lookup | transform_or_validate | RequiresResolvedSourceOrGraph", - "session_callbacks | dispatch | AllowsSourceRange", - "client_request_entry | entrypoint | RequiresResolvedSourceOrGraph", - "shell_completion | terminal_boundary | DiagnosticOnly", - "shell_function_dispatch | dispatch | AllowsLexicalSource", - "shell_installer_bootstrap | entrypoint | AllowsLexicalSource", - "site_lifecycle | entrypoint | RequiresResolvedSourceOrGraph", - "site_reader | state_or_storage | AllowsSourceRange", - "site_terminal | terminal_boundary | AllowsSourceRange", - "sql_relationships | configuration | AllowsLexicalSource", - "sql_tables | state_or_storage | AllowsLexicalSource", - "string_blank_predicate | transform_or_validate | RequiresResolvedSourceOrGraph", - "string_empty_predicate | transform_or_validate | RequiresResolvedSourceOrGraph", - "string_region_handoff | dispatch | RequiresResolvedSourceOrGraph", - ]; - - fn requirement_inventory_entry(requirement: &FlowRequirement) -> String { - format!( - "{} | {} | {:?}", - requirement.id, - requirement.role_id(), - requirement.coverage_mode - ) - } - - #[test] - fn the_requirement_inventory_matches_the_requirement_tables() { - let mut live = all_flow_requirements() - .iter() - .map(requirement_inventory_entry) - .collect::>(); - live.sort(); - live.dedup(); - - let mut recorded = FLOW_REQUIREMENT_INVENTORY - .iter() - .map(|entry| (*entry).to_string()) - .collect::>(); - recorded.sort(); - - let removed = recorded - .iter() - .filter(|entry| !live.contains(entry)) - .collect::>(); - assert!( - removed.is_empty(), - "a requirement disappeared from the tables; a requirement no evidence can reach is a \ - retrieval gap to close, not a requirement to drop: {removed:?}" - ); - let added = live - .iter() - .filter(|entry| !recorded.contains(entry)) - .collect::>(); - assert!( - added.is_empty(), - "a new requirement is not in the checked-in inventory; add it there so removals stay \ - visible in review: {added:?}" - ); - } - - /// One cited anchor that proves each requirement. Two jobs: it shows every requirement is - /// reachable at all (a requirement no evidence can close would report partial forever), and it - /// gives the same-role distinctness test the witnesses it needs. - fn requirement_witnesses() -> Vec<((&'static str, &'static str), AgentCitationDto)> { - vec![ - ( - ("indexing_entrypoint", "entrypoint"), - witness("buildIndex", "src/indexer/build.rs", NodeKind::FUNCTION), - ), - ( - ("indexing_storage", "state_or_storage"), - witness( - "SymbolStore.persist", - "src/store/symbols.rs", - NodeKind::METHOD, - ), - ), - ( - ("request_entrypoint", "registration"), - witness("Router.add_route", "src/routing.py", NodeKind::FUNCTION), - ), - ( - ("request_entrypoint", "entrypoint"), - witness("createInstance", "lib/axios.js", NodeKind::FUNCTION), - ), - ( - ("request_dispatch", "dispatch"), - witness( - "dispatchRequest", - "lib/core/dispatchRequest.js", - NodeKind::FUNCTION, - ), - ), - ( - ("server_route_insertion_handoff", "dispatch"), - witness( - "RouterGroup.handle", - "src/http/router_group.go", - NodeKind::METHOD, - ), - ), - ( - ("server_request_handler_handoff", "entrypoint"), - witness("Server.ServeHTTP", "src/http/server.go", NodeKind::METHOD), - ), - ( - ("server_route_match_lookup", "transform_or_validate"), - witness( - "Engine.handleHTTPRequest", - "src/http/server.go", - NodeKind::METHOD, - ), - ), - ( - ("server_handler_chain_handoff", "terminal_boundary"), - witness( - "Engine.handleHTTPRequest", - "src/http/server.go", - NodeKind::METHOD, - ), - ), - ( - ("request_terminal", "terminal_boundary"), - witness( - "selectAdapter", - "lib/adapters/adapters.js", - NodeKind::FUNCTION, - ), - ), - ( - ("request_terminal", "dispatch"), - witness( - "Session.get_adapter", - "src/requests/sessions.py", - NodeKind::METHOD, - ), - ), - ( - ("request_interceptor_management", "dispatch"), - witness( - "InterceptorManager", - "lib/core/InterceptorManager.js", - NodeKind::CLASS, - ), - ), - ( - ("client_request_entry", "entrypoint"), - witness( - "createClientInstance", - "Source/Session.swift", - NodeKind::FUNCTION, - ), - ), - ( - ("session_callbacks", "dispatch"), - witness( - "SessionDelegate.dispatchEvent", - "Source/SessionDelegate.swift", - NodeKind::METHOD, - ), - ), - ( - ("client_public_facade", "entrypoint"), - witness("createClient", "lib/client.dart", NodeKind::FUNCTION), - ), - ( - ("client_interface_helpers", "entrypoint"), - witness("Client.get", "lib/client.dart", NodeKind::METHOD), - ), - ( - ("client_request_finalization", "transform_or_validate"), - witness( - "BaseRequest.finalize", - "lib/base_request.dart", - NodeKind::METHOD, - ), - ), - ( - ("client_transport_send", "terminal_boundary"), - witness( - "BaseAdapter.send", - "src/requests/adapters.py", - NodeKind::METHOD, - ), - ), - ( - ("client_response_materialization", "terminal_boundary"), - witness("Response.fromStream", "lib/response.dart", NodeKind::METHOD), - ), - ( - ("hook_public_export", "entrypoint"), - witness("useData", "src/index/use-data.ts", NodeKind::FUNCTION), - ), - ( - ("hook_key_serialization", "transform_or_validate"), - witness( - "serializeKey", - "src/_internal/utils/serialize.ts", - NodeKind::FUNCTION, - ), - ), - ( - ("hook_cache_helper", "state_or_storage"), - witness( - "makeCacheHelper", - "src/_internal/utils/helper.ts", - NodeKind::FUNCTION, - ), - ), - ( - ("hook_mutation_flow", "dispatch"), - witness( - "applyMutation", - "src/_internal/utils/mutate.ts", - NodeKind::FUNCTION, - ), - ), - ( - ("command_server_bootstrap", "entrypoint"), - witness("main", "src/server.c", NodeKind::FUNCTION), - ), - ( - ("command_event_loop", "dispatch"), - witness("aeProcessEvents", "src/event/ae.c", NodeKind::FUNCTION), - ), - ( - ("command_network_input", "dispatch"), - witness( - "readQueryFromClient", - "src/networking.c", - NodeKind::FUNCTION, - ), - ), - ( - ("command_dispatch", "dispatch"), - witness("processCommand", "src/server.c", NodeKind::FUNCTION), - ), - ( - ("sql_tables", "state_or_storage"), - witness("CREATE TABLE Artist", "db/schema.sql", NodeKind::FUNCTION), - ), - ( - ("sql_relationships", "configuration"), - witness("FOREIGN KEY", "db/schema.sql", NodeKind::FUNCTION), - ), - ( - ("html_app_shell", "entrypoint"), - witness("div#app", "src/index.html", NodeKind::FUNCTION), - ), - ( - ("css_structure", "configuration"), - witness(":root", "src/main.css", NodeKind::FUNCTION), - ), - ( - ("css_animation_entrypoint", "entrypoint"), - witness( - "@import \"animations/base\"", - "src/animations/index.css", - NodeKind::FUNCTION, - ), - ), - ( - ("css_animation_structure", "configuration"), - witness( - "@keyframes fade-in", - "src/animations/fade.css", - NodeKind::FUNCTION, - ), - ), - ( - ("form_native_constraints", "transform_or_validate"), - witness("required", "examples/form.html", NodeKind::FUNCTION), - ), - ( - ("form_custom_validation", "transform_or_validate"), - witness( - "setCustomValidity", - "examples/validate.js", - NodeKind::FUNCTION, - ), - ), - ( - ("form_submit_guard", "terminal_boundary"), - witness("onSubmitGuard", "examples/submit.js", NodeKind::FUNCTION), - ), - ( - ("shell_installer_bootstrap", "entrypoint"), - witness("nvm_download", "install.sh", NodeKind::FUNCTION), - ), - ( - ("shell_function_dispatch", "dispatch"), - witness("nvm_command_dispatch", "nvm.sh", NodeKind::FUNCTION), - ), - ( - ("shell_completion", "terminal_boundary"), - witness("nvm_completion", "bash_completion.sh", NodeKind::FUNCTION), - ), - ( - ("buffered_storage", "state_or_storage"), - witness("Buffer", "okio/src/buffer.kt", NodeKind::CLASS), - ), - ( - ("buffered_read_write", "dispatch"), - witness("Buffer.writeUtf8", "okio/src/buffer.kt", NodeKind::METHOD), - ), - ( - ("logger_event", "entrypoint"), - witness( - "Logger.addRecord", - "src/logging/Logger.php", - NodeKind::METHOD, - ), - ), - ( - ("handler_processing", "dispatch"), - witness( - "LogProcessingHandler.write", - "src/logging/Handler.php", - NodeKind::METHOD, - ), - ), - // The site object's own lifecycle and output methods. `Build.process` and - // `Renderer.render` stood here before, and both took their subsystem entirely from the - // `lib/site/` folder they were filed in — which is what made a directory look - // load-bearing for this flow. A site generator's build phases hang off the site, and - // that is what the two anchors say now; the folder is left in place so the corpus keeps - // filing off-subject symbols beside them. - ( - ("site_lifecycle", "entrypoint"), - witness("Site.process", "lib/site/site.rb", NodeKind::METHOD), - ), - (("site_reader", "state_or_storage"), { - let mut reader = witness( - "Generator::Reader.read", - "lib/site/reader.rb", - NodeKind::METHOD, - ); - reader.source_excerpt = Some( - "def read\n @site.layouts = LayoutReader.new(site).read\n \ - CollectionReader.new(site).read\n read_data\nend" - .to_string(), - ); - reader - }), - ( - ("site_terminal", "terminal_boundary"), - witness("Site.write", "lib/site/renderer.rb", NodeKind::METHOD), - ), - ( - ("mapper_config", "configuration"), - witness( - "MapperConfiguration", - "src/AutoMapper/MapperConfiguration.cs", - NodeKind::CLASS, - ), - ), - ( - ("mapper_execution", "dispatch"), - witness( - "TypeMapPlanBuilder", - "src/AutoMapper/Execution/Plan.cs", - NodeKind::CLASS, - ), - ), - ( - ("format_arguments", "transform_or_validate"), - witness("basic_format_args", "include/fmt/base.h", NodeKind::CLASS), - ), - ( - ("formatter_fallback", "error_or_fallback"), - witness( - "throw_format_error", - "include/fmt/format.h", - NodeKind::FUNCTION, - ), - ), - ( - ("search_entrypoint", "entrypoint"), - witness("main", "crates/core/main.rs", NodeKind::FUNCTION), - ), - ( - ("search_argument_planning", "configuration"), - witness( - "flags::parse", - "crates/core/flags/mod.rs", - NodeKind::FUNCTION, - ), - ), - ( - ("search_driver", "dispatch"), - witness("search_parallel", "crates/core/main.rs", NodeKind::FUNCTION), - ), - ( - ("search_candidate_traversal", "dispatch"), - witness( - "HiArgs::walk_builder", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_haystack_construction", "transform_or_validate"), - witness( - "HiArgs::haystack_builder", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_matcher_setup", "configuration"), - witness( - "HiArgs::matcher", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_searcher_setup", "configuration"), - witness( - "HiArgs::searcher", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_printer_setup", "configuration"), - witness( - "HiArgs::printer", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_worker_construction", "configuration"), - witness( - "HiArgs::search_worker", - "crates/core/flags/hiargs.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_dispatch", "dispatch"), - witness( - "SearchWorker::execute_search", - "crates/core/search.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_evidence_classification", "transform_or_validate"), - witness( - "SearchHitEvidence::tier", - "crates/core/evidence.rs", - NodeKind::METHOD, - ), - ), - ( - ("search_evidence_output", "terminal_boundary"), - witness( - "append_search_evidence_packet", - "crates/core/output.rs", - NodeKind::FUNCTION, - ), - ), - ( - ("string_blank_predicate", "transform_or_validate"), - witness( - "StringUtils.isBlank", - "src/text/StringUtils.java", - NodeKind::METHOD, - ), - ), - ( - ("string_empty_predicate", "transform_or_validate"), - witness("Strings.isEmpty", "src/text/Strings.java", NodeKind::METHOD), - ), - ( - ("string_region_handoff", "dispatch"), - witness( - "CharSequenceUtils.regionMatches", - "src/text/CharSequenceUtils.java", - NodeKind::METHOD, - ), - ), - ] - } - - fn witness(display_name: &str, file_path: &str, kind: NodeKind) -> AgentCitationDto { - AgentCitationDto { - node_id: NodeId(display_name.to_string()), - display_name: display_name.to_string(), - kind, - file_path: Some(file_path.to_string()), - line: Some(1), - score: 1.0, - origin: SearchHitOrigin::IndexedSymbol, - target: None, - resolvable: true, - subgraph_id: None, - evidence_edge_ids: Vec::new(), - retrieval_score_breakdown: None, - evidence_tier: None, - evidence_producer: None, - resolution_status: None, - loss_reason: None, - coverage_role: None, - eligible_for_sufficiency: Some(true), - source_excerpt: None, - } - } - - #[test] - fn every_requirement_has_evidence_that_can_close_it() { - let witnesses = requirement_witnesses(); - for requirement in all_flow_requirements() { - let key = (requirement.id, requirement.role_id()); - let witness = witnesses - .iter() - .find(|(witness_key, _)| *witness_key == key) - .map(|(_, citation)| citation) - .unwrap_or_else(|| { - panic!( - "requirement {} has no witness; every requirement needs evidence that can \ - close it, or it reports partial forever", - requirement.id - ) - }); - assert!( - requirement.evidence.citation_proves(witness), - "requirement {} is unclosable: its witness `{}` does not satisfy its evidence \ - predicate", - requirement.id, - witness.display_name - ); - } - } - - #[test] - fn client_public_facade_helper_requires_its_exact_outgoing_request_boundary() { - let requirement = CLIENT_PUBLIC_FACADE_REQUIREMENT; - let established_factory = witness("createClient", "lib/client.dart", NodeKind::FUNCTION); - assert!( - requirement - .evidence - .citation_proves_without_call_boundary(&established_factory) - ); - assert!( - requirement - .evidence - .call_boundary_target(&established_factory) - .is_none() - ); - for internal_factory in [ - witness( - "CronetClient._createProfile", - "lib/src/cronet_client.dart", - NodeKind::METHOD, - ), - witness("_createClient", "lib/client.dart", NodeKind::FUNCTION), - witness("createClient", "lib/client.dart", NodeKind::METHOD), - ] { - assert!( - !requirement.evidence.citation_proves(&internal_factory), - "an internal helper is not the package's public facade: {internal_factory:?}" - ); - } - - let helper = witness("request", "src/requests/api.py", NodeKind::FUNCTION); - assert!(requirement.evidence.citation_proves(&helper)); - assert!( - !requirement - .evidence - .citation_proves_without_call_boundary(&helper) - ); - let target = requirement - .evidence - .call_boundary_target(&helper) - .expect("the public helper needs an outgoing CALL boundary"); - assert!(target("Session.request")); - - let other_package_helper = - witness("request", "lib/transportkit/api.py", NodeKind::FUNCTION); - assert!(requirement.evidence.citation_proves(&other_package_helper)); - assert!( - requirement - .evidence - .call_boundary_target(&other_package_helper) - .is_some_and(|target| target("HttpClient.request")) - ); - let library_entry_helper = witness("get", "pkgs/http/lib/http.dart", NodeKind::FUNCTION); - assert!(requirement.evidence.citation_proves(&library_entry_helper)); - assert!( - requirement - .evidence - .call_boundary_target(&library_entry_helper) - .is_some_and(|target| target("_withClient")) - ); - for negative in [ - "request", - "requests.api.request", - "Cache.request", - "CacheClient.request", - "Database.request", - "DatabaseClient.request", - "TelemetryClient.request", - "MonitoringClient.request", - "dispatch_hook", - ] { - assert!( - !target(negative), - "{negative} is not the next request stage" - ); - } - - for wrong_carrier in [ - witness("request", "src/cache.py", NodeKind::FUNCTION), - witness("request", "src/database/api.py", NodeKind::FUNCTION), - witness("request", "src/telemetry/api.py", NodeKind::FUNCTION), - witness("request", "src/monitoring/api.py", NodeKind::FUNCTION), - witness("FrameKind.request", "src/requests/api.py", NodeKind::METHOD), - witness("dispatch_hook", "src/requests/api.py", NodeKind::FUNCTION), - witness("get", "pkgs/http/lib/src/http.dart", NodeKind::FUNCTION), - ] { - assert!(!requirement.evidence.citation_proves(&wrong_carrier)); - assert!( - requirement - .evidence - .call_boundary_target(&wrong_carrier) - .is_none() - ); - } - } - - #[test] - fn hybrid_role_carriers_keep_their_declared_call_boundary() { - let cases = [ - ( - "server request entrypoint", - SERVER_REQUEST_DISPATCH_FLOW[0], - witness("Router.use", "src/router.js", NodeKind::METHOD), - witness("Router.map", "src/router.js", NodeKind::METHOD), - "Router.route", - ), - ( - "server request dispatch", - SERVER_REQUEST_DISPATCH_FLOW[1], - witness("Router.dispatch", "src/router.js", NodeKind::METHOD), - witness( - "RequestDispatcher.execute", - "src/dispatcher.js", - NodeKind::METHOD, - ), - "finalhandler", - ), - ( - "server response terminal", - SERVER_REQUEST_DISPATCH_FLOW[2], - witness("Response.writeBuffer", "src/response.js", NodeKind::METHOD), - witness("ResponseBuffer.read", "src/response.js", NodeKind::METHOD), - "Socket.end", - ), - ( - "client request entrypoint", - CLIENT_REQUEST_DISPATCH_FLOW[0], - witness( - "HttpClientFactory.request", - "src/client.rs", - NodeKind::METHOD, - ), - witness("createClient", "src/client.rs", NodeKind::FUNCTION), - "PreparedRequest.build", - ), - ( - "client public facade", - CLIENT_PUBLIC_FACADE_REQUIREMENT, - witness( - "createClient.request", - "src/requests/api.py", - NodeKind::FUNCTION, - ), - witness("createClient", "src/requests/api.py", NodeKind::FUNCTION), - "Session.request", - ), - ]; - - for (label, requirement, hybrid, role_only, lawful_target) in cases { - assert!( - requirement.evidence.citation_proves(&hybrid), - "{label}: fixture must satisfy the hybrid predicate" - ); - assert!( - !requirement - .evidence - .citation_proves_without_call_boundary(&hybrid), - "{label}: a named role must not bypass carrier boundary proof" - ); - let target = requirement - .evidence - .call_boundary_target(&hybrid) - .unwrap_or_else(|| panic!("{label}: hybrid carrier lost its declared target")); - assert!(target(lawful_target), "{label}: lawful target rejected"); - assert!( - !target("Metrics.record"), - "{label}: unrelated target admitted" - ); - - assert!( - requirement.evidence.citation_proves(&role_only), - "{label}: role-only fixture must retain the established role surface" - ); - assert!( - requirement - .evidence - .citation_proves_without_call_boundary(&role_only), - "{label}: role-only evidence should keep the ordinary CALL contract" - ); - assert!( - requirement - .evidence - .call_boundary_target(&role_only) - .is_none(), - "{label}: role-only evidence must not invent an exact target" - ); - } - - let ordered = CLIENT_REQUEST_DISPATCH_FLOW[1]; - let ordered_hybrid = witness("HttpClient.dispatchSend", "src/client.rs", NodeKind::METHOD); - assert!(ordered.evidence.citation_proves(&ordered_hybrid)); - assert!( - ordered - .evidence - .citation_proves_without_call_boundary(&ordered_hybrid), - "the ordered-boundary predicate keeps its established role behavior" - ); - assert!( - ordered - .evidence - .ordered_call_boundary(&ordered_hybrid) - .is_some() - ); - } - - #[test] - fn request_entrypoint_receipts_reject_self_loops_and_storage_clients() { - let requirement = CLIENT_REQUEST_DISPATCH_FLOW[0]; - let carrier = witness( - "Session.request", - "src/requests/sessions.py", - NodeKind::METHOD, - ); - let receipt = |id: &str, source: NodeId, target: NodeId| GraphEdgeDto { - id: EdgeId(id.to_string()), - source, - target, - kind: EdgeKind::CALL, - confidence: Some(1.0), - certainty: Some("certain".to_string()), - callsite_identity: Some("src/requests/sessions.py:1".to_string()), - candidate_targets: Vec::new(), - }; - - for target in [ - "Session.prepare_request", - "PreparedRequest.build", - "Session.send", - "Client.send", - ] { - let lawful = receipt(target, carrier.node_id.clone(), NodeId(target.to_string())); - assert!( - flow_requirement_call_receipt_is_valid( - &requirement, - &carrier, - &lawful, - target, - NodeKind::METHOD, - ), - "{target}" - ); - } - - for target in [ - "CacheClient.send", - "CacheClient.prepareRequest", - "DatabaseClient.send", - "HookClient.send", - "HookClient.prepareRequest", - ] { - let unlawful = receipt(target, carrier.node_id.clone(), NodeId(target.to_string())); - assert!( - !flow_requirement_call_receipt_is_valid( - &requirement, - &carrier, - &unlawful, - target, - NodeKind::METHOD, - ), - "{target}" - ); - } - - let self_loop = receipt( - "self-loop", - carrier.node_id.clone(), - carrier.node_id.clone(), - ); - assert!(!flow_requirement_call_receipt_is_valid( - &requirement, - &carrier, - &self_loop, - "PreparedRequest.build", - NodeKind::METHOD, - )); - assert!(!ordinary_incident_call_receipt_is_valid( - &carrier, - &self_loop, - NodeKind::METHOD, - )); - } - - #[test] - fn command_loop_requirements_reject_role_shaped_helpers_and_require_exact_handoffs() { - let receipt = |id: &str, source: NodeId, target: NodeId| GraphEdgeDto { - id: EdgeId(id.to_string()), - source, - target, - kind: EdgeKind::CALL, - confidence: Some(1.0), - certainty: Some("certain".to_string()), - callsite_identity: Some("src/runtime.c:1".to_string()), - candidate_targets: Vec::new(), - }; - - let loop_driver = witness("EventLoop.run", "src/runtime.c", NodeKind::FUNCTION); - let loop_call = receipt( - "loop-driver", - loop_driver.node_id.clone(), - NodeId("EventLoop.processEvents".to_string()), - ); - assert!(flow_requirement_call_receipt_is_valid( - &COMMAND_EVENT_LOOP_REQUIREMENT, - &loop_driver, - &loop_call, - "EventLoop.processEvents", - NodeKind::FUNCTION, - )); - let main_driver = witness("aeMain", "src/runtime.c", NodeKind::FUNCTION); - assert!( - !COMMAND_EVENT_LOOP_REQUIREMENT - .evidence - .citation_proves(&main_driver), - "a main-shaped entrypoint is outside the ordinary carrier vocabulary" - ); - assert!( - !COMMAND_EVENT_LOOP_REQUIREMENT - .evidence - .citation_proves_without_call_boundary(&main_driver), - "a main-shaped entrypoint is never direct proof" - ); - let main_loop_call = receipt( - "main-loop-driver", - main_driver.node_id.clone(), - NodeId("aeProcessEvents".to_string()), - ); - assert!(flow_requirement_call_receipt_is_valid( - &COMMAND_EVENT_LOOP_REQUIREMENT, - &main_driver, - &main_loop_call, - "aeProcessEvents", - NodeKind::FUNCTION, - )); - let unrelated_main_call = receipt( - "unrelated-main-call", - main_driver.node_id.clone(), - NodeId("loadConfiguration".to_string()), - ); - assert!(!flow_requirement_call_receipt_is_valid( - &COMMAND_EVENT_LOOP_REQUIREMENT, - &main_driver, - &unrelated_main_call, - "loadConfiguration", - NodeKind::FUNCTION, - )); - let rebind = witness( - "Connection.rebindEventLoop", - "src/runtime.c", - NodeKind::FUNCTION, - ); - assert!( - !COMMAND_EVENT_LOOP_REQUIREMENT - .evidence - .citation_proves(&rebind) - ); - - let command_router = witness("processCommand", "src/server.c", NodeKind::FUNCTION); - let routing_call = receipt( - "command-router", - command_router.node_id.clone(), - NodeId("rejectCommand".to_string()), - ); - assert!(flow_requirement_call_receipt_is_valid( - &COMMAND_DISPATCH_REQUIREMENT, - &command_router, - &routing_call, - "rejectCommand", - NodeKind::FUNCTION, - )); - for helper in ["processCommandAndResetClient", "ModuleCommandDispatcher"] { - assert!( - !COMMAND_DISPATCH_REQUIREMENT - .evidence - .citation_proves(&witness(helper, "src/server.c", NodeKind::FUNCTION)), - "{helper} must not close central command routing" - ); - } - } - - #[test] - fn command_loop_queries_name_the_carrier_action_and_exact_boundary() { - assert!( - COMMAND_EVENT_LOOP_REQUIREMENT - .query_seeds - .contains(&"main event loop process events") - ); - assert!( - COMMAND_DISPATCH_REQUIREMENT - .query_seeds - .contains(&"process command routing checks") - ); - } - - #[test] - fn server_route_detail_requires_each_exact_ordered_handoff() { - let requirement = |id: &str| { - SERVER_ROUTE_DISPATCH_DETAIL_FLOW - .iter() - .copied() - .find(|requirement| requirement.id == id) - .unwrap_or_else(|| panic!("missing route requirement {id}")) - }; - let receipt = |id: &str, source: NodeId, target: NodeId| GraphEdgeDto { - id: EdgeId(id.to_string()), - source, - target, - kind: EdgeKind::CALL, - confidence: Some(1.0), - certainty: Some("certain".to_string()), - callsite_identity: Some("src/http/router.rs:1".to_string()), - candidate_targets: Vec::new(), - }; - let cases = [ - ( - "server_route_insertion_handoff", - witness( - "RouterGroup.handle", - "src/http/router_group.go", - NodeKind::METHOD, - ), - "Engine.addRoute", - "SegmentTree.add", - ), - ( - "server_request_handler_handoff", - witness("Server.ServeHTTP", "src/http/server.go", NodeKind::METHOD), - "Engine.handleHTTPRequest", - "SearchEngine.handleRequest", - ), - ( - "server_route_match_lookup", - witness( - "Engine.handleHTTPRequest", - "src/http/server.go", - NodeKind::METHOD, - ), - "node.getValue", - "Config.getValue", - ), - ( - "server_handler_chain_handoff", - witness( - "Engine.handleHTTPRequest", - "src/http/server.go", - NodeKind::METHOD, - ), - "Context.Next", - "RenderContext.Next", - ), - ]; - - for (id, carrier, lawful_target, hostile_target) in cases { - let requirement = requirement(id); - assert!(requirement.evidence.citation_proves(&carrier), "{id}"); - let lawful = receipt( - id, - carrier.node_id.clone(), - NodeId(lawful_target.to_string()), - ); - assert!( - flow_requirement_call_receipt_is_valid( - &requirement, - &carrier, - &lawful, - lawful_target, - NodeKind::METHOD, - ), - "{id} should retain its exact next stage" - ); - let hostile = receipt( - hostile_target, - carrier.node_id.clone(), - NodeId(hostile_target.to_string()), - ); - assert!( - !flow_requirement_call_receipt_is_valid( - &requirement, - &carrier, - &hostile, - hostile_target, - NodeKind::METHOD, - ), - "{id} must reject unrelated {hostile_target}" - ); - } - } - - #[test] - fn requirements_sharing_a_flow_role_stay_separable_by_evidence() { - let witnesses = requirement_witnesses(); - let witness_for = |requirement: &FlowRequirement| { - let key = (requirement.id, requirement.role_id()); - witnesses - .iter() - .find(|(witness_key, _)| *witness_key == key) - .map(|(_, citation)| citation.clone()) - .unwrap_or_else(|| panic!("missing witness for {key:?}")) - }; - - let mut checked_pairs = 0; - for (group, requirements) in all_flow_requirement_groups() { - for (index, left) in requirements.iter().enumerate() { - for right in requirements.iter().skip(index + 1) { - if left.role != right.role || left.id == right.id { - continue; - } - checked_pairs += 1; - let left_witness = witness_for(left); - let right_witness = witness_for(right); - assert!( - !right.evidence.citation_proves(&left_witness), - "in flow {group}, evidence for {} also closes its {} sibling {}: two \ - requirements sharing a role must not be closed by one anchor", - left.id, - left.role.label(), - right.id - ); - assert!( - !left.evidence.citation_proves(&right_witness), - "in flow {group}, evidence for {} also closes its {} sibling {}: two \ - requirements sharing a role must not be closed by one anchor", - right.id, - right.role.label(), - left.id - ); - } - } - } - assert!( - checked_pairs >= 5, - "the tables still contain same-role sibling requirements; this invariant must actually \ - be exercising them (checked {checked_pairs})" - ); - } - - /// Symbols of the kind retrieval turns up in any repository, none of which prove anything about - /// any flow requirement in the tables. - /// - /// The positive witnesses above only show each predicate accepts *one* hand-picked anchor. They - /// cannot see a predicate that also accepts everything else, and that is exactly what happened: - /// `citation_owns_formatter_fallback` matched any symbol whose name contained "error" anywhere in - /// the repository, `citation_owns_hook_public_export` matched any name starting with the three - /// letters "use", and `citation_owns_form_native_constraint` matched the unanchored substring - /// "min" — so `CliParseError`, `userProfile` and `adminPanel` each closed a requirement they - /// have nothing to do with, and packets carrying them published as sufficient. - /// - /// Every entry must be rejected by every requirement. Adding a needle to a carrier without - /// checking it here is how the next false-safe verdict gets in. - fn unrelated_repository_symbols() -> Vec { - vec![ - witness("CliParseError", "src/cli/parse.cc", NodeKind::FUNCTION), - witness("assert_valid_utf8", "src/text/utf8.rs", NodeKind::FUNCTION), - witness("panic_hook", "src/runtime/panic.rs", NodeKind::FUNCTION), - witness("failToOpenSocket", "src/net/socket.go", NodeKind::FUNCTION), - witness("userProfile", "src/session/user.ts", NodeKind::FUNCTION), - witness("useragentString", "src/http/headers.ts", NodeKind::FUNCTION), - witness("determineFieldOrder", "src/layout.js", NodeKind::FUNCTION), - witness("adminPanel", "src/admin.js", NodeKind::FUNCTION), - witness("terminalWidth", "src/tty.js", NodeKind::FUNCTION), - witness("submitTelemetry", "src/telemetry.js", NodeKind::FUNCTION), - witness("Cache.write", "lib/cache.rb", NodeKind::METHOD), - witness("Uri.prepare", "lib/uri.dart", NodeKind::METHOD), - witness("ProjectSettings", "src/settings.rs", NodeKind::STRUCT), - witness("parseTimestamp", "src/time/parse.ts", NodeKind::FUNCTION), - witness("RowIterator", "src/db/rows.rs", NodeKind::STRUCT), - witness("MigrationRunner", "src/db/migrate.rb", NodeKind::CLASS), - // Each of these closed a requirement at exactly this path. The first six are role - // classified, where the *directory* assigned the role: `/views/` and `/app/` mean route - // handling, `store` means persistence, `/flags/` means argument planning. The last four - // sit inside the very flow they were accepted by, which is the case a corpus of - // symbols from elsewhere in the repository can never reach. - witness("Store.delete", "src/store/store.rs", NodeKind::METHOD), - witness( - "serializeSettings", - "src/store/serialize.ts", - NodeKind::FUNCTION, - ), - witness("readManifest", "src/store/manifest.rs", NodeKind::FUNCTION), - witness("renderChart", "src/views/chart.js", NodeKind::FUNCTION), - witness("Cache.write", "app/views/cache.rb", NodeKind::METHOD), - witness( - "FeatureFlags.options", - "src/flags/feature.rs", - NodeKind::METHOD, - ), - witness("handleClick", "src/logging/ui.php", NodeKind::FUNCTION), - witness( - "createUserRecord", - "src/logging/audit.php", - NodeKind::FUNCTION, - ), - witness("use_temp_dir", "src/index/tmp.ts", NodeKind::FUNCTION), - witness("Store.get", "lib/client.dart", NodeKind::METHOD), - // Each of these closed a requirement one level below the last round's fix. The first - // four are role-classified and the *file name* assigned the role — `runtime.c`, - // `signal_dispatch.rs`, `store.ts` — after the directories had already been stripped. - // The rest are carrier-backed, and each is a compound noun whose head is the flow's own - // subject word: a form's `min`, a logger's `handler`, a site's `layout`, a build's - // `post`, a buffer. - witness("tooltipHandler", "src/os/runtime.c", NodeKind::FUNCTION), - witness( - "panicHandler", - "src/os/signal_dispatch.rs", - NodeKind::FUNCTION, - ), - witness( - "workspaceSettings", - "src/config/store.ts", - NodeKind::FUNCTION, - ), - witness( - "MathSymbolTable", - "src/math/table_dispatch.rs", - NodeKind::STRUCT, - ), - witness("clampMin", "src/forms/layout.ts", NodeKind::FUNCTION), - witness( - "PaymentHandler.process", - "src/logging/payments.php", - NodeKind::METHOD, - ), - witness( - "Layout.render", - "src/components/layout.tsx", - NodeKind::METHOD, - ), - witness("readFile", "src/assets/io.ts", NodeKind::FUNCTION), - witness( - "PostMortem.generate", - "src/crash/report.rb", - NodeKind::METHOD, - ), - witness("FrameBuffer", "src/gfx/frame.cpp", NodeKind::STRUCT), - witness("SegmentTree.read", "src/algo/segtree.rs", NodeKind::METHOD), - witness( - "sourceMapOptions", - "src/build/config.ts", - NodeKind::FUNCTION, - ), - witness("RoadMapPlanner", "src/nav/planner.rs", NodeKind::STRUCT), - witness("dispatchRider", "src/delivery/rider.ts", NodeKind::FUNCTION), - witness( - "validationMinScore", - "src/auth/password.ts", - NodeKind::FUNCTION, - ), - witness("ChartAdapter", "src/charts/adapter.ts", NodeKind::CLASS), - ] - } - - /// Shapes of symbol name a repository is full of, none of which is evidence for any step in any - /// flow in the tables. - /// - /// These are families, not examples, and each one is a way a predicate here has been fooled or - /// could be. A **verb-named accessor** meets a carrier that matched the HTTP method set on a - /// symbol's terminal segment, so every `.get`, `.delete` and `.options` in the repository was a - /// client's request method. A **`handle*` callback** meets a carrier that matched "handle" as a - /// prefix of "handler", so every front end's click and scroll handlers were a logging - /// framework's record processing. A **`*Record` builder** meets a carrier that matched the word - /// "record", so every database row constructor was a logger's record creation. A **snake- or - /// kebab-cased `use_*`** meets a carrier that treated `_` and `-` as the front-end hook naming - /// convention. The last family is ordinary vocabulary from subsystems no flow here covers. - /// - /// The property that makes a name a negative, and the bar a new entry has to clear, is that no - /// requirement's *two* factors are both satisfied by it. Sharing one is allowed and is the point: - /// `Cache.write` names a step word the site build reads, `Matrix.post` names one of its subjects, - /// `Store.get` names an HTTP verb — and each must still be rejected, because none of them names - /// both. A name that names both is not a negative; it is evidence. - fn off_subject_symbol_names() -> Vec<(&'static str, NodeKind)> { - let mut names = Vec::new(); - for name in [ - "Store.get", - "Store.delete", - "Cache.put", - "FeatureFlags.options", - "Queue.head", - "Matrix.post", - "Palette.patch", - ] { - names.push((name, NodeKind::METHOD)); - } - for name in [ - "handleClick", - "handleKeypress", - "handleDragStart", - "handleScroll", - "handleResize", - ] { - names.push((name, NodeKind::FUNCTION)); - } - for name in [ - "createUserRecord", - "createDnsRecord", - "addBillingRecord", - "makeInventoryRecord", - ] { - names.push((name, NodeKind::FUNCTION)); - } - for name in ["use_temp_dir", "use-legacy-mode", "use_default_locale"] { - names.push((name, NodeKind::FUNCTION)); - } - for name in [ - "compareVersions", - "parseTimestamp", - "TooltipAnchor", - "ColorPalette", - "computeChecksum", - "encodeBase64", - "serializeSettings", - "readManifest", - "renderChart", - "MigrationRunner", - "RowIterator", - "ProjectSettings", - "adminPanel", - "terminalWidth", - "determineFieldOrder", - "userProfile", - "submitTelemetry", - "Uri.prepare", - "Cache.write", - ] { - names.push((name, NodeKind::FUNCTION)); - } - names - } - - /// Every directory the corpus places an off-subject symbol in. - /// - /// The first half is derived from the witness table, so every flow's *own* folder is covered - /// and stays covered as requirements are added — a symbol sitting beside a flow's real evidence - /// is the case a corpus of symbols from elsewhere in the repository cannot reach, and path - /// tokens are what re-open a scoped predicate. The second half is every path fragment the - /// shared evidence-role classifier will assign a role from on its own, read out of - /// `packet_evidence_roles`: those directories hand out a role to whatever is filed in them. - fn off_subject_directories() -> Vec { - let mut directories = Vec::new(); - let mut push = |directory: String| { - if !directories.contains(&directory) { - directories.push(directory); - } - }; - for ((_, _), witness) in requirement_witnesses() { - let path = witness.file_path.clone().unwrap_or_default(); - push(match path.rfind('/') { - Some(index) => path[..index + 1].to_string(), - None => String::new(), - }); - } - for directory in [ - "src/routes/", - "src/router/", - "src/controllers/", - "src/views/", - "src/pages/", - "app/", - "app/views/", - "src/event/", - "src/events/", - "src/flags/", - "src/protocol/", - "src/networking/", - "src/runtime/", - "src/store/", - "src/indexer/", - "src/workspace/", - "src/interceptors/", - "src/dispatch/", - "src/collections/", - "src/source_group/", - // The same directories a Windows citation arrives with. Two code paths disagree about - // separators — the role classifier normalizes them, the carriers lowercase and replace - // them, and stripping a directory has to split on both — so the corpus carries both. - "app\\views\\", - "src\\store\\", - "src\\runtime\\", - ] { - push(directory.to_string()); - } - directories - } - - /// The extensions the corpus crosses its directories with. - /// - /// Derived from the witness paths, minus the document surfaces. A stylesheet, a markup - /// document, a schema file and a shell script are proved *by the file*: their anchors are - /// selectors, attributes and statements, not identifiers, and "a code identifier inside a - /// `.css` file" is not a citation retrieval can produce. Those requirements are still exercised - /// by this corpus — they have to reject every code path in it. - fn off_subject_code_extensions() -> Vec { - let document_surfaces = [ - ".css", ".scss", ".sass", ".less", ".html", ".htm", ".sql", ".sh", - ]; - let mut extensions = Vec::new(); - for ((_, _), witness) in requirement_witnesses() { - let path = witness.file_path.clone().unwrap_or_default(); - let Some(index) = path.rfind('.') else { - continue; - }; - let extension = path[index..].to_ascii_lowercase(); - if document_surfaces.contains(&extension.as_str()) || extensions.contains(&extension) { - continue; - } - extensions.push(extension); - } - extensions - } - - /// The generated corpus: every off-subject name, in every flow's directory and every - /// role-granting directory, under every code extension, as every kind of behavior owner. - fn generated_off_subject_symbols() -> Vec { - let mut symbols = Vec::new(); - for (name, kind) in off_subject_symbol_names() { - for directory in off_subject_directories() { - for extension in off_subject_code_extensions() { - for owner_kind in [ - kind, - NodeKind::CLASS, - NodeKind::STRUCT, - NodeKind::INTERFACE, - NodeKind::CONSTANT, - ] { - symbols.push(witness( - name, - &format!("{directory}elsewhere{extension}"), - owner_kind, - )); - } - } - } - } - symbols - } - - /// A bare `map` is not an object mapper, and the family that rides in on it is large. - /// - /// `MapPlanner` used to be documented here as an accepted limitation: `mapper_execution` asks - /// for an object mapper and an execution plan, and both words were literally in the name. But - /// the word carrying the subsystem was `map`, which is the head of `sourceMap`, `roadMap`, - /// `siteMap`, `heatMap` and `tileMap` — so the limitation was not one name, it was every - /// compound noun in software ending in "map", and `sourceMapOptions` (in every JavaScript build - /// configuration there is) plus `RoadMapPlanner` closed the whole two-step flow between them. - /// - /// A bare `map` now has to say what it maps. `TypeMapPlanBuilder`, the real anchor, does. - #[test] - fn a_map_that_is_not_an_object_mapper_closes_nothing() { - let requirement_named = |id: &str| { - all_flow_requirements() - .into_iter() - .find(|requirement| requirement.id == id) - .unwrap_or_else(|| panic!("{id} should be in the tables")) - }; - - for (display_name, kind) in [ - ("MapPlanner", NodeKind::STRUCT), - ("RoadMapPlanner", NodeKind::STRUCT), - ("SiteMapPlan", NodeKind::STRUCT), - ("TileMapExecutor", NodeKind::STRUCT), - ("sourceMapOptions", NodeKind::FUNCTION), - ("HeatMapConfig", NodeKind::STRUCT), - ("bitmapPipeline", NodeKind::FUNCTION), - ] { - for path in ["src/store/planner.rs", "src/mapping/plan.rs", "src/nav.ts"] { - let anchor = witness(display_name, path, kind); - for id in ["indexing_storage", "mapper_execution", "mapper_config"] { - assert!( - !requirement_named(id).evidence.citation_proves(&anchor), - "`{display_name}` at `{path}` is not {id}: the word carrying the subsystem \ - is the head of a compound noun from another domain" - ); - } - } - } - - let real = witness( - "TypeMapPlanBuilder", - "src/AutoMapper/Execution/Plan.cs", - NodeKind::CLASS, - ); - assert!( - requirement_named("mapper_execution") - .evidence - .citation_proves(&real), - "a type map's plan builder is still the mapper's execution step" - ); - } - - /// The complete set of *bare, one-word* symbol names that close a requirement, as - /// `requirement | word`. - /// - /// A one-word name carries no second factor: there is no room in it for both "which subsystem - /// is this" and "which step of it". So every entry here is a word that, on its own, anywhere in - /// any repository, under any directory and any language, proves a step. - /// - /// This list is **not** the whole surface, and it used to claim to be. Every predicate in this - /// crate matches whole tokens *inside* a name, so a word that closes a requirement bare closes - /// it inside compounds too — `buffer` here meant `FrameBuffer` and `ZBuffer` as well, and the - /// list said nothing about it. `COMPOUND_EVIDENCE_SURFACE` above is the family version and is - /// the one to read for what an unrelated symbol can still be mistaken for; this one is the - /// stricter subset, kept because a *bare* word closing a requirement is a sharper signal. - /// - /// Each of these words *is* the requirement's subject: a class named `Buffer` is the buffer, a - /// function named `main` is the entrypoint, a method named `request` is the client's request - /// method. That is the intended reading of a name-driven predicate. What must not happen is the - /// list growing quietly: an entry appearing here means some carrier's two factors collapsed - /// into one word, which is how `renderChart` proved a site renderer and every `.get` in the - /// repository proved a client's convenience method. - /// - /// The stylesheet, markup and shell entries arrived when the sweep started crossing every - /// surface class a carrier branches on rather than `.rs` and `.ts`. They are not a collapse: - /// on those surfaces the anchor is a selector, an attribute or a shell function, which is the - /// declared exception, and no code identifier can reach them because the extension gate will - /// not have it. What the widening was for is the case that is *not* an exception — a `.vue` - /// anchor was taking the markup branch while the `.ts` beside it took the name branch, and a - /// sweep that only ever asked about `.ts` could not see the difference. - const ONE_WORD_EVIDENCE_SURFACE: &[&str] = &[ - "buffered_storage | buffer", - "client_interface_helpers | request", - "command_server_bootstrap | main", - "css_animation_entrypoint | forward", - "css_animation_entrypoint | import", - "css_animation_entrypoint | use", - "css_animation_structure | animation", - "css_animation_structure | delay", - "css_animation_structure | duration", - "css_animation_structure | fillmode", - "css_animation_structure | iteration", - "css_animation_structure | keyframes", - "css_animation_structure | transition", - "form_custom_validation | validity", - "hook_mutation_flow | mutat", - "hook_mutation_flow | mutate", - "hook_mutation_flow | mutation", - "html_app_shell | app", - "html_app_shell | body", - "html_app_shell | main", - "html_app_shell | module", - "html_app_shell | mount", - "html_app_shell | root", - "html_app_shell | script", - "html_app_shell | shell", - "indexing_storage | indexer", - "indexing_storage | indexers", - "indexing_storage | snapshot", - "indexing_storage | snapshots", - "indexing_storage | symbol", - "indexing_storage | symbols", - "request_entrypoint | asgi", - "request_entrypoint | route", - "request_entrypoint | router", - "request_entrypoint | routers", - "request_entrypoint | routes", - "request_entrypoint | servlet", - "request_entrypoint | wsgi", - "search_entrypoint | main", - "shell_completion | alias", - "shell_completion | compgen", - "shell_completion | complete", - "shell_completion | completion", - "shell_installer_bootstrap | bootstrap", - "shell_installer_bootstrap | download", - "shell_installer_bootstrap | install", - "shell_installer_bootstrap | setup", - "shell_installer_bootstrap | source", - "shell_installer_bootstrap | sources", - ]; - - /// Every word any predicate in this crate reads, so the sweep below covers the whole vocabulary - /// the tables are written in rather than a sample of it. Held to the carriers' own source by - /// `the_one_word_sweep_covers_every_word_the_carriers_match_on`, so it cannot fall behind them. - fn evidence_vocabulary() -> Vec { - let mut vocabulary = vec![ - "request", - "requests", - "route", - "routes", - "router", - "routing", - "tree", - "node", - "controller", - "handler", - "handlers", - "next", - "proceed", - "endpoint", - "server", - "middleware", - "http", - "protocol", - "dispatch", - "dispatcher", - "wsgi", - "asgi", - "rack", - "servlet", - "gateway", - "ui", - "view", - "widget", - "client", - "clients", - "instance", - "factory", - "session", - "transport", - "adapter", - "adapters", - "send", - "fetch", - "url", - "connection", - "response", - "socket", - "stream", - "writer", - "sink", - "buffer", - "sender", - "task", - "delegate", - "index", - "indexer", - "indexing", - "symbol", - "symbols", - "snapshot", - "workspace", - "candidate", - "catalog", - "ingest", - "crawl", - "serve", - "daemon", - "bootstrap", - "startup", - "init", - "main", - "listen", - "listener", - "event", - "events", - "loop", - "poll", - "select", - "choose", - "epoll", - "reactor", - "tick", - "network", - "networking", - "query", - "wire", - "command", - "commands", - "table", - "exec", - "execute", - "search", - "searcher", - "grep", - "match", - "matcher", - "args", - "argv", - "arg", - "worker", - "printer", - "log", - "logger", - "logging", - "record", - "records", - "site", - "page", - "post", - "layout", - "template", - "document", - "collection", - "static", - "theme", - "asset", - "renderer", - "generator", - "build", - "builder", - "pipeline", - "process", - "run", - "start", - "generate", - "render", - "write", - "read", - "output", - "emit", - "map", - "mapper", - "mapping", - "typemap", - "plan", - "execution", - "config", - "profile", - "option", - "options", - "format", - "formatter", - "fmt", - "vformat", - "error", - "throw", - "fail", - "assert", - "fallback", - "panic", - "cache", - "caches", - "helper", - "key", - "keys", - "serialize", - "mutate", - "mutation", - "form", - "validate", - "validity", - "guard", - "submit", - "required", - "pattern", - "min", - "max", - "install", - "setup", - "download", - "completion", - "prepare", - "prepared", - "finalize", - "materialize", - "interceptor", - "storage", - "persist", - "manifest", - "get", - "put", - "patch", - "delete", - "head", - "https", - "transports", - "sends", - "finaliz", - "finalis", - "prepar", - "to", - "body", - "responses", - "bytes", - "settle", - "settled", - "transform", - "materiali", - "use", - "serializ", - "serialis", - "hash", - "stable", - "stringify", - "helpers", - "provider", - "context", - "state", - "store", - "make", - "creat", - "mutat", - "app", - "root", - "shell", - "module", - "script", - "mount", - "import", - "forward", - "keyframes", - "animation", - "transition", - "duration", - "delay", - "iteration", - "fillmode", - "string", - "strings", - "text", - "char", - "sequence", - "sequences", - "charsequence", - "charsequences", - "blank", - "whitespace", - "empty", - "region", - "regions", - "matches", - "matching", - "compare", - "equal", - "equals", - "forms", - "fieldset", - "validation", - "validations", - "validates", - "invalid", - "constraint", - "constraints", - "guards", - "preventdefault", - "minlength", - "maxlength", - "inputtype", - "inputmode", - "validator", - "customvalid", - "checkvalid", - "reportvalid", - "submits", - "submitt", - "source", - "case", - "compgen", - "complete", - "alias", - "segment", - "reads", - "writes", - "emits", - "flush", - "skip", - "copy", - "copyto", - "readfrom", - "writeto", - "logs", - "loggers", - "handle", - "finalhandler", - "add", - "create", - "initialize", - "new", - "application", - "invoke", - "end", - "finish", - "res", - "reply", - "push", - "pop", - "remove", - "set", - "register", - "batch", - "interface", - "sites", - "pages", - "posts", - "layouts", - "templates", - "documents", - "collections", - "themes", - "assets", - "data", - "file", - "files", - "html", - "phases", - "writ", - "outputs", - "renders", - "maps", - "mappers", - "mappings", - "execut", - "formats", - "formatters", - "formatting", - "printf", - "sprintf", - "fprintf", - "arguments", - "value", - "values", - "err", - "indexes", - "indexed", - "indexers", - "snapshots", - "workspaces", - "candidates", - "catalogs", - "routers", - "controllers", - "endpoints", - "servers", - "cgi", - "fastcgi", - "instances", - "factories", - "sessions", - "urls", - "connections", - "sockets", - "streams", - "tasks", - "delegates", - "loops", - "polling", - "kqueue", - "queries", - "searches", - "matchers", - // The IO peers a byte buffer sits between, the record-pipeline words a logging - // framework qualifies its handler classes with, and the model words an object mapper - // maps. Each became a way to satisfy a subsystem factor this round, so each has to be - // swept as a name in its own right. - "sources", - "sinks", - "byte", - "io", - "reader", - "input", - "pipe", - "channel", - "abstract", - "base", - "default", - "generic", - "null", - "noop", - "interfaces", - "impl", - "implementation", - "processing", - "processor", - "processors", - "entry", - "entries", - "formatted", - "group", - "chain", - "stack", - "type", - "types", - "object", - "objects", - "model", - "models", - "entity", - "entities", - "dto", - "dtos", - "member", - "members", - "property", - "properties", - "destination", - "class", - "classes", - // A `site` beside a `map` is a sitemap, which the static-site carriers now reject. The - // rejecting word has to be swept too: a word that *narrows* a carrier is a word whose - // removal widens it, and the sweep is what would notice. - "sitemap", - "sitemaps", - // Public-facade carrier scope and its exact close negatives. - "api", - "hook", - "hooks", - "database", - "db", - "metrics", - "telemetry", - "monitoring", - "observability", - // Exact public-facade successor `_withClient` is the bounded wrapper that owns the - // client lifetime before delegating to `Client`. - "with", - ] - .into_iter() - .map(str::to_string) - .collect::>(); - vocabulary.extend(carrier_taxonomy_vocabulary()); - vocabulary.sort(); - vocabulary.dedup(); - vocabulary - } - - /// The sweep is only as wide as the vocabulary it sweeps, so the vocabulary is checked against - /// the carriers' own source instead of being maintained beside them by hand. - /// - /// Every bare lowercase word a carrier matches on is a word that can move a predicate on its - /// own. A word present there and absent here is a blind spot in the sweep — and it would sit - /// exactly where the next widening lands, because a widening *is* a word being added to a - /// carrier. - #[test] - fn the_one_word_sweep_covers_every_word_the_carriers_match_on() { - let vocabulary = evidence_vocabulary(); - let mut missing: Vec = Vec::new(); - for line in include_str!("packet_evidence_carriers.rs").lines() { - let code = line.trim_start(); - if code.starts_with("#[cfg(test)]") { - // Below here are the carriers' own fixtures, whose literals are anchors rather - // than needles. - break; - } - if code.starts_with("//") { - continue; - } - for (index, literal) in code.split('"').enumerate() { - if index % 2 == 0 - || literal.len() < 2 - || !literal - .chars() - .all(|character| character.is_ascii_lowercase()) - || vocabulary.iter().any(|word| word.as_str() == literal) - || missing.iter().any(|word| word == literal) - { - continue; - } - missing.push(literal.to_string()); - } - } - assert!( - missing.is_empty(), - "these words move a carrier but are never swept as a one-word symbol name, so the \ - recorded surface below cannot see what they admit: {missing:?}" - ); - } - - /// Nouns from domains no flow in the tables covers. - /// - /// Crossing them with the evidence vocabulary builds the compound names a repository is - /// actually full of — `FrameBuffer`, `sourceMapOptions`, `PaymentHandler`, `symbolFont` — which - /// is the shape the bare-word sweep below cannot see. - fn off_subject_qualifiers() -> Vec<&'static str> { - vec![ - "Frame", "Road", "Payment", "Math", "Picker", "Chart", "Pixel", "Crash", "Coupon", - "Rider", - ] - } - - /// One file per *surface class* a carrier branches on, in each directory that used to hand out - /// a subsystem. - /// - /// This replaces a directory list crossed with `[".rs", ".ts"]` and a comment asserting that - /// "`.ts` stands for every script surface". That was not true and the sweep could not see it: - /// `is_form_validation_surface`, `is_markup_document` and `is_stylesheet` each branch on the - /// extension into a *path-reading* code path, and `.vue` took the markup branch, so every - /// symbol in a component library's `forms/` directory inherited the form factor from its folder - /// while the sweep only ever asked about `.rs` and `.ts`. Nothing here is asserted any more: - /// `the_sweeps_stand_for_every_surface_a_carrier_branches_on` checks each of these against - /// every extension a carrier actually reads. - /// - /// Fewer directories than the bare-word sweep uses, and deliberately so: after - /// `role_survives_without_its_path` no directory can grant a role at all, and the one invariant - /// that still has to see every directory — `no_requirement_is_closed_by_an_unrelated_repository_symbol` - /// — already crosses the full list. What is left that reads a path is the declared document - /// exception, so this is the repository root, a plain source directory, and the folders that a - /// carrier used to take a subsystem from: the static-site subject word, the `static/` spelling - /// of it, a logging folder and a form example folder. - fn sweep_surfaces() -> Vec<&'static str> { - vec![ - "one.rs", - "src/one.ts", - "lib/site/one.rs", - "lib/site/one.ts", - "public/static/one.ts", - "src/logging/one.rs", - "examples/form/one.ts", - "examples/form/one.vue", - "examples/form/one.html", - "src/styles/one.css", - "scripts/install", - "db/one.sql", - ] - } - - /// The file names the bare-word sweep crosses its (much longer) directory list with: one per - /// surface class, for the same reason as above. - fn sweep_surface_files() -> Vec<&'static str> { - vec![ - "one.rs", "one.ts", "one.vue", "one.html", "one.css", "one.sh", "one.sql", "install", - ] - } - - /// Every extension a carrier reads, paired with the sweep file that stands for it. - /// - /// The left column is checked against the carriers' own source, so an extension added to a - /// carrier without a representative here fails the gate rather than opening a hole in it. The - /// right column is checked *behaviourally*: a representative that stopped behaving like the - /// extension it stands for is exactly the `.vue`-as-markup defect, and it now fails. - fn carrier_surface_classes() -> Vec<(&'static str, &'static str)> { - vec![ - // Script surfaces. `.vue` and `.svelte` are here rather than with the markup documents - // because the indexer blanks a single-file component's template and parses only its - // `