Skip to content

Commit c9a9e92

Browse files
claudehyperpolymath
authored andcommitted
feat(typecheck): detect duplicate function / type / agent declarations
Roadmap item 1 (parser/typecheck gap-fill). Extends the `DuplicateDefinition` check from locale/choreography to the remaining name-bearing top-level decls. `fn`, `type`, and `agent` declarations were *gathered* but silently *last-win* on redefinition — two `fn f`, two `type T`, or two `agent A` were accepted with the second quietly shadowing the first. They now error. Detection is per parsed top-level declaration and within-kind, so an `agent Name(args)` reference inside a `supervisor children:` list (the agent-creates-agent pattern used by several examples) is NOT a declaration and is correctly not flagged. Verified against every example — zero false positives. 4 new typechecker tests (duplicate fn / type / agent, and the agent-reference-in-supervisor regression). 901 oo7-core lib tests pass; fmt + clippy clean. https://claude.ai/code/session_018CaSgNjNURC7ocsyjYh9We
1 parent 06033e6 commit c9a9e92

5 files changed

Lines changed: 72 additions & 5 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
@state(version="2.0"):
88
phase: "implementation"
9-
next_action: "Layer-10 rung 3b (cross-handler residues via agent state partly runtime, ResidueCell/takeForReverse:Maybe). Also: extend duplicate-definition detection to the other name-bearing decls (functions/types/agents currently last-wins on redefinition). Rocq-9 proof-stack install in progress (see below) to run the rocq canonical-proof-suite locally."
10-
last_action: "Governance + handover artifacts (branch claude/affirmation-and-handover): added AFFIRMATION.adoc (root, RSR-convention timestamped attestation of current standing — musts met, intends in_progress, blocked/open declared honestly), docs/status/must-intend-wish-2026-06-19.adoc (the full Echidna + 007 must/intend/wish tables), and docs/handover/NEXT-CLAUDE-PROMPT.md (paste-ready handoff with env gotchas). Followed confirmed RSR conventions (SPDX+AsciiDoc+a2ml attestation+contractile vocab); standards-repo AFFIRMATION template not retrievable (standards public but out of session MCP scope; web search 0 results). Banned-token-clean (reworded the believe_me/sorry/etc. examples). PRIOR completed this session: runner awk POSIX-portability fix \\s->[[:space:]] (PR #47 merged) — suite now runs locally; ROCQ 9.0 installed locally (OPAMROOT=/root/.opam3 switch rocq9, via github opam-repository mirror) → 35/37 canonical .v compile; last 3 (mathcomp chain + Coquelicot) walled by github-only network (menhir@gitlab.inria.fr, cmdliner@erratique.ch, coq-released@coq.inria.fr all 403) — CI-only. Echidna = unspecified ecosystem placeholder (no spec). Prior PRs: #46 (import+locale+choreography dup-detection), #45 (just gate), #43 (L10 proofs gated), #42 (rung-3a)."
9+
next_action: "User roadmap (in order): (1) parser/typecheck gap-fill [DONE: function/type/agent dup-detection this branch] — remaining gap-fill candidates: the `trace { }` standalone-statement parse gap (semantic_analysis_example.007:71 fails), and extend dup-detection to protocols/behaviours/supervisors if wanted. (2) L10 RUNG 3b — cross-handler residues via agent state (partly runtime: residue cell in agent state + evaluator Holding/Spent, ResidueCell/takeForReverse:Maybe). (3) FULL PROOF SUITE GREEN — write M5-yoneda.agda (only agda entry, not-started/stub); the 3 rocq holdouts (M2 mathcomp, E1_ndim+S3_ndim Coquelicot) are CI-only by network policy. (4) CONSOLIDATE."
10+
last_action: "Roadmap item 1 — extended DuplicateDefinition to FUNCTION/TYPE/AGENT (branch claude/dup-detection-fn-type-agent). These were gathered but silently last-win on redefinition; check_duplicate_definitions now flags two top-level fn/type/agent of the same name (within-kind, per parsed decl, so an `agent Name(args)` ref in a supervisor children: list is NOT a duplicate — verified no false positives across all examples). 4 new tests; 901 oo7-core lib pass; fmt+clippy clean. Also re-verified machine-checking with the local toolchain: ROCQ 35/38 .v compile (3 network-walled), AGDA 0/1 (M5-yoneda not-started), IDRIS2 16/16 incl L10 (EchoResidue+EchoResidueLinear). Prior this session: governance/handover docs (#48), runner awk fix (#47), Rocq 9 local (35/37), #46 import+locale+choreography dup-detection, #45 just gate. Echidna still unspecified placeholder."
1111
updated: 2026-06-19T21:45:00Z
1212

1313
@blockers:

.machine_readable/session-log.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@
4141
[2026-06-18 13:20:34] Gap-fill (a): LOCALE + CHOREOGRAPHY duplicate-name detection on the same branch (extends PR #46 to "duplicate detection for the silently-skipped decls"). Added TypeErrorKind::DuplicateDefinition{kind,name} + check_duplicate_definitions pass (Pass 1c in check_program + _snapshot) flagging two same-named locale or choreography decls; agent_api DUPLICATE_DEFINITION (rename_or_remove). 3 tests (dup-locale, dup-choreography, distinct-locales-OK); 897 oo7-core lib pass; fmt+clippy clean; locales.007/choreography.007/imports.007 all still check OK. Ran cargo test directly (NOT just gate) to avoid assail polluting session-log. ROCQ-9 install attempt 3 progressing in background (/tmp/rocq3.log, OPAMROOT=/root/.opam3): github-mirror opam init succeeded, rocq9 switch building OCaml 4.14.2 then coq.9.0.0+mathcomp from github source; Coquelicot blocked (gitlab.inria.fr 403).
4242

4343
[2026-06-19 21:47:43] Governance + handover artifacts (branch claude/affirmation-and-handover, off latest main 2c52cf6). User asked to (1) store the must/intend/wish status tables in-repo, (2) write a paste-ready prompt for the next 007 Claude, (3) make an AFFIRMATION.adoc per standards-repo guidance, timestamped. Delivered: AFFIRMATION.adoc (root) — RSR-convention attestation (SPDX+AsciiDoc+a2ml attestation ethos+contractile must/intend/wish vocab), timestamp 2026-06-19T21:43:26Z, commit 2c52cf6; affirms musts met (Harvard, L1-L10, honest proofs, RSR hygiene), labels intends in_progress (rung 3b, gap-fill, full suite) and the two owner-side blocks (Actions billing, github-only network) honestly. docs/status/must-intend-wish-2026-06-19.adoc — the full Echidna (thin: placeholder, no spec) + 007 tables. docs/handover/NEXT-CLAUDE-PROMPT.md — paste-ready handoff incl. env gotchas (billing-blocked CI+admin-merge, github-only network, OPAMROOT=/root/.opam3 rocq9 switch, assail-pollutes-session-log, branch/gate discipline). standards-repo AFFIRMATION template NOT retrievable (public but out of session MCP scope; web search 0 results; tree listings errored) so followed confirmed conventions + said so in the doc. Reworded believe_me/sorry/etc. examples out of the docs to stay banned-token-clean. Doc-only; A2ML 6a2 clean. Pushing -> draft PR.
44+
45+
[2026-06-19 23:08:35] Roadmap item 1 (parser/typecheck gap-fill) — extend DuplicateDefinition to FUNCTION/TYPE/AGENT (branch claude/dup-detection-fn-type-agent off main). These three are gathered (gather_function/type/agent_decl) but silently last-win on redefinition; added Function/TypeDecl/Agent arms to check_duplicate_definitions (within-kind, keyed on (kind,name), per parsed top-level decl). SAFETY: text-grep flagged apparent dup agent names in 5 examples, but inspection showed the 2nd occurrence is always `agent Name(args)` in a supervisor children: list (a REFERENCE, not a top-level decl) — so the per-decl check ignores them; confirmed empirically (oo7 check on every example → zero false positives). 4 tests (dup fn/type/agent + agent-reference-in-supervisor-not-a-dup); fixed the regression snippet after learning `agent Name(args)` is only valid in supervisor children:, not as a control_stmt. 901 oo7-core lib pass (was 897); fmt+clippy clean. Also (answering user q on "done intra-body"): re-verified machine-checking with local provers — ROCQ 35/38 .v (3 walled: M2 mathcomp, E1_ndim+S3_ndim Coquelicot), AGDA 0/1 (M5-yoneda not-started), IDRIS2 16/16 incl L10. User roadmap remaining: (2) L10 rung 3b, (3) full proof suite green (write M5 agda; rocq holdouts CI-only), (4) consolidate. Pushing -> draft PR.

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
1313
- **`just gate` — one-command, billing-independent local green-check.** Runs every locally-faithful CI stage in sequence (rustfmt, clippy, `cargo test`, grammar-check, verify-harvard, e2e, `idris2 --check` of all 12 Idris proofs), continues past failures, and prints a per-stage PASS/FAIL/SKIP summary with a non-zero exit on any FAIL. Stages needing an absent toolchain (idris2) or an external GitHub action (A2ML/K9 validators, security scans) self-skip with a notice rather than failing, so it runs anywhere; the rocq canonical-proof-suite (needs Rocq 9) and external manifest/security scans stay CI/nightly-only. Verified GREEN on `main`. Distinct from `just ci` (build/test/lint/verify simulation) and `just check` (fast fmt/lint/test pre-commit).
1414
- **Import binding-collision detection.** The type checker now validates `import` declarations: every binding an import introduces — an `as` alias, each selective `from … import` item, or a bare `import a.b.c`'s last segment (`c`) — must be unique across the program. A collision (e.g. `import a as x` + `import b as x`, or `from m import abs, abs`) is now a `DuplicateImport` error instead of being silently accepted (imports were previously skipped by the gather pass entirely). Surfaced to agents via `agent_api` code `IMPORT_DUPLICATE_BINDING` (+`rename_import`/`remove_duplicate` remediations). Complements the existing `import_resolver` (which only detects *circular* imports). 4 new typechecker tests (894 `oo7-core` lib tests passing); `examples/imports.007` and the other import-bearing examples are unaffected.
1515
- **Duplicate locale / choreography detection.** `locale` and `choreography` declarations were likewise silently skipped by the gather pass, so two declarations sharing a name were accepted. Two `locale x = …` (or two `choreography y(…) { … }`) now raise a `DuplicateDefinition` error (agent_api code `DUPLICATE_DEFINITION`). 3 new typechecker tests (897 `oo7-core` lib tests passing); `examples/locales.007` and `examples/choreography.007` are unaffected.
16+
- **Duplicate function / type / agent detection.** Extends `DuplicateDefinition` to the name-bearing decls that were *gathered but silently last-win* on redefinition: two top-level `fn f`, two `type T`, or two `agent A` now error. Detection is per parsed top-level declaration and within-kind, so an `agent Name(args)` reference in a `supervisor children:` list (the agent-creates-agent pattern) is correctly *not* a duplicate. Verified against every example (no false positives). 4 new typechecker tests (901 `oo7-core` lib tests passing; fmt + clippy clean).
1617
- **752 tests** (up from 207 at 0.5.0). CRG-C achieved 2026-04-04 for `oo7-core`.
1718
- **31 multi-language backends** via `mk2_bridge::create_backend_registry()`. Real dispatch to `elixir`, `idris2`, `gleam`, `rescript`, `nickel`, `guile`, `psql`, etc. Tier-5 coprocessor backends (CUDA/Vulkan/Metal/OpenCL/FPGA/TPU/DSP) do phase-1 device discovery with documented "kernel launch is future work" status.
1819
- **Metainterpreter** (`metainterpreter.rs`, ~3,075 LOC): reify/reflect/step/replay, four modes, 40 inline tests, 18 benches.

crates/oo7-core/src/typechecker.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4674,15 +4674,23 @@ fn check_import_bindings(program: &Program, env: &mut TypeEnv) {
46744674
}
46754675

46764676
/// Validate that no two top-level declarations of the same kind share a name.
4677-
/// Covers `locale` and `choreography` declarations — previously skipped by the
4678-
/// gather pass, so duplicates were silently accepted. A collision makes the
4679-
/// name ambiguous and is reported as a `DuplicateDefinition` error.
4677+
/// Covers `locale`, `choreography`, `function`, `type`, and `agent`
4678+
/// declarations. The locale/choreography kinds were skipped by the gather pass
4679+
/// entirely; the function/type/agent kinds were gathered but last-win on
4680+
/// redefinition (silently). A within-kind collision makes the name ambiguous
4681+
/// and is reported as a `DuplicateDefinition` error. Detection is per parsed
4682+
/// top-level declaration, so an `agent Name(args)` spawn/instantiation inside a
4683+
/// body is not a declaration and never collides with the `agent Name { … }`
4684+
/// that declares it.
46804685
fn check_duplicate_definitions(program: &Program, env: &mut TypeEnv) {
46814686
let mut seen: HashSet<(&'static str, String)> = HashSet::new();
46824687
for decl in &program.declarations {
46834688
let (kind, name) = match decl {
46844689
TopLevelDecl::Locale(ld) => ("locale", ld.name.clone()),
46854690
TopLevelDecl::Choreography(cd) => ("choreography", cd.name.clone()),
4691+
TopLevelDecl::Function(fd) => ("function", fd.name.clone()),
4692+
TopLevelDecl::TypeDecl(td) => ("type", td.name.clone()),
4693+
TopLevelDecl::Agent(ad) => ("agent", ad.name.clone()),
46864694
_ => continue,
46874695
};
46884696
if !seen.insert((kind, name.clone())) {

crates/oo7-core/src/typechecker_tests.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,3 +2096,59 @@ fn distinct_locales_are_not_an_error() {
20962096
);
20972097
}
20982098
}
2099+
2100+
// ── Duplicate function / type / agent detection ──────────────────────
2101+
//
2102+
// These were gathered but silently last-win on redefinition. Two top-level
2103+
// declarations of the same kind sharing a name now raise DuplicateDefinition.
2104+
// An `agent Name(args)` spawn inside a body is NOT a declaration and must not
2105+
// collide with the `agent Name { … }` that declares it.
2106+
2107+
#[test]
2108+
fn duplicate_function_is_error() {
2109+
let src = "@impure fn f() { let x = 1 }\n@impure fn f() { let y = 2 }";
2110+
let errs = parse_and_check(src).expect_err("expected a duplicate-function error");
2111+
assert!(
2112+
has_duplicate_definition(&errs),
2113+
"two `fn f` must collide, got: {:?}",
2114+
errs
2115+
);
2116+
}
2117+
2118+
#[test]
2119+
fn duplicate_type_is_error() {
2120+
let src = "type Url = String\ntype Url = Int";
2121+
let errs = parse_and_check(src).expect_err("expected a duplicate-type error");
2122+
assert!(
2123+
has_duplicate_definition(&errs),
2124+
"two `type Url` must collide, got: {:?}",
2125+
errs
2126+
);
2127+
}
2128+
2129+
#[test]
2130+
fn duplicate_agent_is_error() {
2131+
let src = "agent A { control { on receive(x: Int) { let y = x } } }\nagent A { control { on receive(x: Int) { let z = x } } }";
2132+
let errs = parse_and_check(src).expect_err("expected a duplicate-agent error");
2133+
assert!(
2134+
has_duplicate_definition(&errs),
2135+
"two `agent A` must collide, got: {:?}",
2136+
errs
2137+
);
2138+
}
2139+
2140+
#[test]
2141+
fn agent_reference_in_supervisor_is_not_a_duplicate() {
2142+
// `agent Name(args)` in a supervisor `children:` list references the agent;
2143+
// it is not a second top-level declaration and must not collide with the
2144+
// `agent Name { … }` that declares it. (This is the real shape that the
2145+
// examples use — agent_creates_agent.007, full_language.007, etc.)
2146+
let src = "agent Worker(caps: Cap[reason]) { control { on receive(x: Int) { let y = x } } }\nsupervisor Sup {\n strategy: one_for_all\n max_restarts: 3 per 60 s\n children: [\n agent Worker(caps: [reason]),\n ]\n}";
2147+
if let Err(errs) = parse_and_check(src) {
2148+
assert!(
2149+
!has_duplicate_definition(&errs),
2150+
"an agent reference in a supervisor must NOT raise DuplicateDefinition, got: {:?}",
2151+
errs
2152+
);
2153+
}
2154+
}

0 commit comments

Comments
 (0)