Skip to content

[Task]: Signal a vocabulary decision when a term is coined, not only after the tree is scanned #4743

Description

@DJC1412

Task ID or area

area: semantics / control-plane vocabulary — coin-time decision signal (follow-up to #4447)

Technical direction

Core control-plane hardening; Architecture and research incubator

Intent

Open the question #4447's 2026-09-18 tracker update named but deliberately did not track: "how an agent notices it is coining a new term and decides whether it should". That update states the acceptance test — can an agent coining a term in the unregistered majority get a signal? — and says it needs its own issue rather than another milestone on the convergence tracker. This is that issue. It is a design request for maintainer direction; I am not starting implementation before one.

Credit where the measurement is due: the census numbers below are @songoow's, recomputed on 092a28c4e in that comment. Everything marked measured here I re-ran on current main myself.

Goal and acceptance gap

The apparatus classifies a shared vocabulary after it has already drifted. Nothing asks the question at the moment the term appears.

Measured here on current main:

  • loopx/semantics/vocabulary_v0.json registers 26 vocabularies, each carrying meaning, tier, status, owners, values, value_notes; coverage_floor records vocabularies: 26, owner_symbols: 51, literal_scan_fields: 1. Only 6 declare producers and 1 declares literal_scan — consistent with the tracker's "6 carry producer evidence (0.9%)" against 664 vocabulary carriers in the tree.
  • The disposition taxonomy already exists and is already validated for shape: formal_model.candidate_decisions = compatibility_only, create_vocabulary, extend_vocabulary, external_input, local_only, reuse_existing, unknown, default unknown. Its own meaning says it is advisory with "no per-candidate storage or runtime enforcement".
  • The string candidate_decision occurs in the registry exactly once — at its own definition. No vocabulary records the disposition it was admitted under.
  • The one live consumer is review-time, not coin-time: loopx/capabilities/pr_review_queue/result_check.py:158 reads candidate_decision, and that capability's README requires it for aligned / new_semantics_justified / advisory / not_yet_proven rows. So the label is asked for when a change is already up for review.
  • The instruction surfaces that every agent reads before writing code mention vocabulary governance 0 times: grep -ci vocabularAGENTS.md 0, CONTRIBUTING.md 0, docs/development/testing-and-quality.md 1.
  • The only firing check is the full-tree scan at commit/premerge/CI time. On a source checkout without npm ci --ignore-scripts, examples/semantic-vocabulary-drift-smoke.py does not even reach a verdict: it aborts after ~9s inside loopx/semantics/production.py:140 with TypeScript production parser failed. A coin-time signal that inherits that dependency cannot run where agents actually work.

So the gap is not a missing enum, a missing scanner, or a missing document. It is that the only moment the system speaks is after the term exists in code, reviewed by someone else, at full-tree cost — and on some machines not at all.

Proposed scope

Three boundaries, each independently testable, and none of them a fourth scanner revision (#4447 rules further scan precision out of scope; the existing apparatus is already ~4,145 lines of scanner plus ~4,123 of its own tests plus a ~3,506-line bilingual RFC, and it has repaid 7 forked names and 17 duplicate definitions).

  1. Registration admits a disposition. A vocabulary entry in vocabulary_v0.json carries which candidate_decisions value it was coined under, validated by the check that already validates that file. This closes "the taxonomy is defined and used zero times" without new machinery, and it keeps coverage_only_grows honest because a new create_vocabulary is then distinguishable from an extend_vocabulary after the fact.
  2. A diff-scoped probe, not a wider scan. Reuse the on-demand inventory the RFC's Q9 already chose: given changed paths, report newly-appearing classification literals that collide with or duplicate a registered vocabulary's value set, and print the disposition question. Cost proportional to the diff. Explicitly not: a resident watcher, a new store, or a second source of vocabulary truth.
  3. Say it where agents read. One obligation paragraph in AGENTS.md (and the matching pointer from CONTRIBUTING.md): coining a state-classification or protocol literal names its disposition and reuses an existing owner or justifies a new one. Whether that is machine-enforced or guidance must be stated as such in the text, per the repository's own guidance-versus-obligation rule — I would start with guidance plus the probe, and only promote to enforced once the probe's false-positive rate is measured.

Decisive acceptance question, in the form the tracker asked for: can an agent coining a term in the unregistered majority get a signal, does it arrive before the commit-time full-tree scan, and what does it cost? A fixture answers the first two; a timed run against a changed-files set answers the third.

Intended base branch

main.

Relevant files or commands

  • loopx/semantics/vocabulary_v0.json (formal_model.candidate_decisions, coverage_floor, policy)
  • examples/semantic-vocabulary-drift-smoke.py (registry shape validation today)
  • scripts/generate_semantic_inventory.py (the on-demand inventory Q9 selected)
  • docs/architecture/rfcs/semantic-vocabulary-convergence-v0.md (+ .zh-CN), §10 policy record
  • AGENTS.md, CONTRIBUTING.md, docs/development/testing-and-quality.md
  • loopx/capabilities/pr_review_queue/result_check.py (existing candidate_decision consumer)

Validation plan

Not executed — this issue proposes, it does not deliver. The plan for whichever slice is admitted:

Slice Decisive validation
Registry disposition examples/semantic-vocabulary-drift-smoke.py green with the field present, plus a mutation case: a vocabulary whose admitted_as is not one of the seven values fails closed. Existing 26 entries keep passing without rewriting history.
Diff-scoped probe A synthetic changed-file that coins a term colliding with a registered value set produces the disposition question and exits non-zero; the same file with an explicit reuse_existing justification passes. Timed against a normal-size diff and reported.
Instruction surfaces examples/docs-governance-smoke.py plus the existing locale-parity check, and the public/private scan over the changed doc paths.
Cross-cutting tests/architecture/, and one run on a checkout without npm ci so the probe's dependency floor is explicit rather than assumed.

Public/private boundary

  • This issue does not include private benchmark traces, verifier output, credentials, internal document links, raw agent sessions, or local runtime state.
  • I will not run or duplicate maintainer-owned benchmark cases unless a maintainer explicitly splits out a public task.

Questions for the owner and @songoow

  1. Is the registry-disposition field (1) welcome as a standalone first PR, or should it wait for (2) so the value is produced by a real caller rather than back-filled?
  2. Should the probe live behind loopx check, behind the existing inventory script, or as a pr_review_queue input — the last option reuses the consumer that already asks for candidate_decision at review time.
  3. Guidance or enforcement for AGENTS.md: the review lenses flag prose-only obligations, so I would rather state it as guidance plus a machine-checked probe than write a machine-enforced-sounding sentence that nothing enforces.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions