You are doing rigorous empirical-SE rule extraction for a research artifact. Work strictly from the
data file; do NOT invent rules or quotes.

INPUT: docs/experimental/ruleset/candidates_categorized.tsv
  Tab-separated, columns: repo<TAB>family<TAB>line_no<TAB>category_guess<TAB>text
  Each row is one candidate "behavioral constraint" line already extracted (with provenance) from a
  real CLAUDE.md/AGENTS.md across 101 distinct popular AI-agent repos.

TASK: Cluster these 529 candidate lines into 30-50 CANONICAL, ENFORCEABLE behavioral rules for the
ActPlane OS-level agent harness. ActPlane enforces at the syscall layer and can observe/enforce:
  exec (with argv tokens), file open/read/write/unlink, network connect (IPv4),
  taint propagation (file-read taints reader, write taints file, connect carries taint),
  conditions (lineage-includes exec G, after exec G, target-scope), declassify/endorse on a gate exec,
  boolean label masks (req AND / forbid NOT, including "A and B" multi-label).

INCLUSION (keep a cluster only if ALL hold):
  - It constrains agent BEHAVIOR (must/must-not/only/before/without), not coding style or build docs.
  - It is ENFORCEABLE: Observable at the syscall layer (a real exec/open/write/connect) AND
    Expressible in the constructs above. Drop purely conversational ("ask the user first"),
    PR-etiquette, or subjective code-quality rules.

For EACH canonical rule output ONE JSON object (JSONL, one per line) with EXACTLY these fields:
  id            : "R01", "R02", ...
  canonical     : one-sentence statement of the rule (English)
  category      : a D1 category label (e.g. secrets-egress, vcs-gating, mediation, test-before,
                  readonly, destructive, network-egress, workspace, task-isolation, approval-gate)
  freq_repos    : integer = number of DISTINCT repos (col 1) among the candidate lines you assigned
                  to this cluster
  example_repos : array of up to 4 {"repo","family","line_no","quote"} taken VERBATIM from input rows
                  (quote = the exact `text` field; do not paraphrase). These are the provenance.
  dsl           : the ActPlane DSL encoding (string with \n). Use: source/rule/deny OP TARGET [@arg]
                  [if EXPR] [unless COND] / reason / effect kill / declassify|endorse. Prefer kill.
  dsl_constructs: array, e.g. ["exec-sink","@arg","object-source","taint-flow","connect-sink",
                  "lineage","after","declassify","multi-label","target-scope"]
  complexity_tier: 1 (single op+label), 2 (+condition or object source), 3 (taint-flow / declassify /
                   multi-label). PREFER producing many tier-2 and tier-3 rules — exercise the full engine.
  enforceable   : "observable+expressible"
  inducible     : true if a violation can be deliberately triggered in a test, else false

RULES:
  - Output ONLY JSONL (one JSON object per line), nothing else. No prose, no markdown fences.
  - Every quote MUST be copied verbatim from a real input row (repo+family+line_no must match that row).
  - Aim for 30-50 rules; ensure coverage of the major categories present and a strong share of tier 2/3.
  - Sort by freq_repos descending.

Write the JSONL to the file docs/experimental/ruleset/raw_codex_extraction.jsonl (create it).
