{
  "manifest_version": "0.1",
  "work_slug": "drduke-datadictionary-type-by-table",
  "concept_ark_suffix": "dp-dr-duke-s-phytochemical-and-ethnobotanical-databases",
  "protocol": "protocol:secondary-analysis 0.1",
  "module": "agent/tools.py",
  "analysis_unit": "analysis.py",
  "published_results": "results.json",
  "source_dataset_doi": "10.15482/USDA.ADC/1239279",
  "environment": {
    "language": "python",
    "tested_version": "3.13",
    "requirements": "requirements-reproduction.txt",
    "network": "deny-except-pinned-inputs"
  },
  "execution": {
    "boundary": "docs/72-reproduction-contract.md",
    "live_invocation": "not connected; the sandboxed runner gates execution over MCP"
  },
  "surface_note": "analysis.py is decomposed where it matters: load_fields, mc_chi2 and holm are called directly. The three family members exist as three calls to mc_chi2 inside analysis.main(), so pre_registered_family rebuilds that orchestration, and its published_pointer calls are the proof that it reproduces results.json block for block. The pinned 20 KB dictionary is fetched through scripts/fetch_pinned.py rather than analysis.acquire_data, whose broad except falls back to a path that exists on one machine. fig-2 (standardised residuals) and fig-3 (the seed + 1 null sample) publish no number in results.json, so neither is exposed as a tool: a tool here must reproduce a published value. Every Monte-Carlo call at the published 20,000 replicates takes tens of seconds, because mc_chi2 rebuilds a pandas crosstab per replicate; results are cached per argument set.",
  "tools": [
    {
      "name": "source_profile",
      "summary": "Identity and size of the pinned data dictionary, as results.json records it.",
      "unit": "analysis.load_fields and analysis._md5 over the pinned file (fetched by scripts/fetch_pinned.py against analysis.CANONICAL_URL and analysis.EXPECTED_MD5)",
      "parameters": [],
      "returns": "seed, mc_replicates, source_doi, source_md5 (recomputed over the bytes read), n_tables, n_columns",
      "supports_claims": [1, 4],
      "deterministic": true
    },
    {
      "name": "degenerate_fields",
      "summary": "Variance of the required and size fields, the two the object declines to test.",
      "unit": "analysis.main: degenerate_fields block over analysis.load_fields",
      "parameters": [],
      "returns": "required.distinct_values; size.within_type_unique_sizes and size.sizes_by_type (the sizes themselves, not published, returned so the claim can be read off)",
      "supports_claims": [4],
      "deterministic": true
    },
    {
      "name": "association_test",
      "summary": "Monte-Carlo chi-square of table against declared type, over a chosen slice of the columns.",
      "unit": "analysis.mc_chi2 over analysis.load_fields",
      "parameters": [
        {
          "name": "type_grouping",
          "type": "string",
          "default": "declared",
          "range": "declared (Short Text, Double, Long Integer, Date With Time) | text_vs_nontext (Short Text against the other three, the R2 collapse)",
          "published_values": ["declared", "text_vs_nontext"]
        },
        {
          "name": "drop_columns",
          "type": "array of string | null",
          "default": null,
          "range": "any column names present in the dictionary, matched case-insensitively; each must exist",
          "published_values": [null, ["ID"]],
          "notes": "R1 drops ID, the Long Integer key present once in every table. CREATED, the only Date With Time column, is present once in 9 of the 15 tables and is the other structural column; the published run keeps it."
        },
        {
          "name": "exclude_tables",
          "type": "array of string | null",
          "default": null,
          "range": "any subset of ACTIVITIES, AGGREGAC, ASSAY, CHEMICALS, CHEM_MEANS, CODES, COMMON_NAMES, DOSAGE, ETHNOBOT, FARMACY_NEW, FNFTAX, PARTS, REFERENCES, SUPERACT, YIELDS that leaves at least two tables",
          "published_values": [null]
        },
        {
          "name": "mc_replicates",
          "type": "integer",
          "default": 20000,
          "range": ">= 1; the add-one p-value cannot fall below 1 / (mc_replicates + 1)",
          "published_values": [20000]
        },
        {
          "name": "seed",
          "type": "integer",
          "default": 20240607,
          "range": ">= 0",
          "published_values": [20240607, 20240609, 20240610],
          "notes": "The published run seeds the primary with 20240607, R1 with 20240609 and R2 with 20240610. Only p_mc depends on the seed; chi2, dof, both Cramer's V and the contingency table do not."
        }
      ],
      "returns": "chi2, dof, p_mc, cramers_v, cramers_v_bias_corrected, n, n_tables, type_levels, n_cells, cells_expected_lt5, contingency, and the design echoed back",
      "supports_claims": [1, 2, 3],
      "deterministic": true
    },
    {
      "name": "pre_registered_family",
      "summary": "The pre-registered family of three (primary, R1, R2), Holm-corrected, laid out as results.json.",
      "unit": "analysis.main: three calls to analysis.mc_chi2 on streams seed, seed + 2, seed + 3, then analysis.holm",
      "parameters": [
        {
          "name": "exclude_tables",
          "type": "array of string | null",
          "default": null,
          "range": "any subset of the 15 tables that leaves at least two; applied to every member",
          "published_values": [null]
        },
        {
          "name": "mc_replicates",
          "type": "integer",
          "default": 20000,
          "range": ">= 1, applied to every member",
          "published_values": [20000]
        },
        {
          "name": "seed",
          "type": "integer",
          "default": 20240607,
          "range": ">= 0; the base stream, members use seed, seed + 2 and seed + 3 as the published run does",
          "published_values": [20240607]
        }
      ],
      "returns": "primary, R1_drop_id and R2_binary_text blocks with p_holm, and the multiple_comparison block, in the shape of results.json",
      "supports_claims": [1, 2, 3],
      "deterministic": true
    },
    {
      "name": "holm",
      "summary": "Holm-Bonferroni correction over a family of p-values, in input order.",
      "unit": "analysis.holm",
      "parameters": [
        {
          "name": "p_values",
          "type": "array of number | null",
          "default": null,
          "range": "any non-empty list of p-values in [0, 1]; null uses the published family's Monte-Carlo p-values in the order primary, R1, R2",
          "published_values": [null]
        }
      ],
      "returns": "the corrected p-values, in input order",
      "supports_claims": [1, 2, 3],
      "deterministic": true
    }
  ]
}
