mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-03 08:06:01 +08:00
* docs(guidance): repo-wide agent-guidance audit — fix drift, prune 21.5k lines, consolidate tests/ onto AGENTS.md convention
Full-layer audit of the agent-guidance system (root contracts, .claude rules/
skills/commands, family and crate AGENTS.md, CONTRACT specs, tests guidance,
docs/internal), verified reference-by-reference against HEAD.
- Fix stale/ghost references: UserSandboxProcessPort, ProductSurfaceError,
LlmError::ContextLengthExceeded, INVERTED_PORT_IMPLEMENTORS, split channel
traits (ChannelIngress/ChannelReply/ChannelDelivery), memory-native's
never-implemented EmbeddingProvider seam, wrong layer/crate/module counts.
- Convert unpinned prose numbers to regeneration commands or pinning-test
citations across root, family, and crate guidance (drift-proofing).
- tests/: rename CLAUDE.md -> AGENTS.md with CLAUDE.md symlinks (crates/
convention), extend scripts/ci/check-guidance.py discovery to tests/,
delete stale e2e scenario tables, dedupe tier taxonomy against
.claude/rules/testing.md.
- Commands/skills: delete six dead v1 commands (add-tool, review-pr,
review-crate, fix-issue, respond-pr, add-sse-event) and the v1-teaching
architecture-video skill; convert ironclaw-reborn-skill-maintainer into
the auto-loading rule .claude/rules/guidance-maintenance.md; fix clippy
-D warnings and portable date in surviving commands; triggers-only
frontmatter; add automations section to reborn-feature.
- Rules: rename gateway-events.md -> events.md; revive
scripts/check-type-duplicates.py (glob matched zero types since the
family reorg); index all 15 rules in root AGENTS.md for Codex parity.
- docs/internal: delete 70 superseded plans/specs/design docs (~21.5k
lines, each re-verified unreferenced); fix misleading v1-migration
status lines; rewrite the contracts index as a recipe; restore two docs
that proved live-referenced.
- Trim composition CONTRACT.md route-mirror sections (invariants kept).
Verified: check-guidance.py (384 files, 0 grandfathered),
docs_publication_boundary.py, cargo test -p ironclaw_architecture_tests,
scripts/ci test-plan suite (87/87) — all green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(guidance): address PR #7797 review comments
Verified each of the ~40 bot/reviewer findings against the tree; applied the
valid mechanical fixes, rebutted the rest with evidence (see PR comment).
- Test planner: add renamed tests/ guidance aliases to IGNORED_GUIDANCE_PATHS
(reproduced the fail-closed abort on this PR's own changed-file list) and
extend the planner test to all six guidance paths.
- check-guidance: add self-tests proving tests/-tree discovery and alias
enforcement (48 tests, was 46); new self-test file for
check-type-duplicates.py (4 tests).
- Portability: replace GNU-only date -d in triage commands with a python3
one-liner (works on macOS BSD and Linux).
- Count/claim accuracy: product_contracts manager-port prose 4 -> 3 (matches
INVERTED_PORT_IMPLEMENTORS), kernel grep -cF for literal #[test] (was regex
char class, 2 vs 23), rg -o|wc -l for a true total in architecture.md,
Rust-scoped LlmProvider count (catches 5 generic impls), measured
1/73-crate dual-backend claim in pr-shepherd, executable wc -l in
assistant guidance, AST/pytest recipes for the e2e test-count figures.
- Content: deslop co-author line no longer hardcodes an address; ship.md
surfaces Postgres-skip counts; risk-label guidance documents the crates/**
labeler blind spot; e2e authoring recipe leads with reborn_v2_* fixtures;
stale CLAUDE.md line citation replaced with a stable anchor; ✎ provenance
notes for two deleted-plan citations; unified-channel-model status text
reconciled with an explicit ChannelDelivery-only exception note.
Gates: check-guidance (384 files, 0 grandfathered), docs boundary, planner
tests 87/87, check-guidance self-test 48/48, type-dup self-test 4/4 - green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ci): classify scripts/test-check-type-duplicates.py in the PR test planner
The self-test added in 77bc8f8c5 was never registered in
PR_STATIC_CONTROL_PATHS, so the planner's fail-closed unmapped-path arm
aborted 'Detect Reborn test scope' and cascaded into the whole Reborn
matrix skipping. Classified like its subject (deliberately CI-unwired
local dev tool, per the existing entry's rationale) and pinned in the
static-control planner test alongside it.
Verified: planner tests OK; planner run against this PR's full
changed-file list now returns mode=selected with the path owned by
static checks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(docs): drop parity-QA path reference from tests/integration/AGENTS.md
The guidance dedup in this PR added a literal
tests/support/reborn_parity_qa reference to tests/integration guidance,
which scripts/ci/check-test-suite-boundaries.sh correctly flags: the
one-way dependency guard covers docs too, and origin/main's version of
this file carried no such reference. Fix the content, not the check -
the tier comparison is reworded to describe the RebornBinaryE2EHarness
seam difference without naming the parity/QA tree.
Verified: check-test-suite-boundaries.sh OK; check-guidance OK; the
full 'Detect Reborn test scope' job reproduced locally end-to-end.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: run the type-duplicates self-test in Code Style; exercise the production report path
Address the two follow-up review findings on 77bc8f8c5/9ed30caf8:
- Wire scripts/test-check-type-duplicates.py into code_style.yml's
Static-check self-tests step (next to test-check-guidance.py) so the
regression test is actually enforced; update the planner's
PR_STATIC_CONTROL_PATHS comment accordingly (classification unchanged
- Code Style is the static lane).
- Strengthen the semantic-duplicate self-test to also drive the
production main() report path and assert on its printed candidate
output, instead of only re-computing similarity locally. Strictly
stronger; the other three tests are untouched.
Verified: self-test 4/4, planner tests 87/87, workflow-contract
self-test 94/94, planner simulation over both changed paths classifies
cleanly (no unmapped-path abort).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: trigger fast-checks on type-duplicates script changes; symbol recipe for the tier reference
- code_style.yml's has_code filter covered scripts/ci/ but not the bare
scripts/ type-duplicates pair, so a diff touching only the new
self-test skipped the fast-checks job that runs it (the previous
commit's 'runs unconditionally' claim was wrong - corrected the
planner comment too). Added the two files to the filter following the
check_no_panics precedent and pinned them as in_scope probes in the
ws12 workflow-contracts routing test.
- tests/integration/AGENTS.md tier reference is now re-verifiable via a
single-hit symbol recipe (rg 'struct RebornBinaryE2EHarness') instead
of a path citation, which the test-suite boundary guard forbids from
this subtree.
Verified: ws12 workflow contracts 94/94, planner tests 87/87,
check-test-suite-boundaries OK, check-guidance OK.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Henry Park <16583448+henrypark133@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
94 lines
3.4 KiB
Python
Executable File
94 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Detect cross-crate SEMANTIC duplicate-type candidates by field/variant signature.
|
|
|
|
Name matching misses the real failure mode (same DTO job, different names), so
|
|
this compares (field-name, field-type) sets for structs and variant sets for
|
|
enums across crates and reports pairs above a similarity threshold.
|
|
|
|
Output is CANDIDATES, not verdicts: a match is either a true duplicate (unify
|
|
into its owner), a justified mirror (independent wire/domain evolution), or a
|
|
coincidental shape. Judge each pair by reading the definitions before acting.
|
|
See .claude/rules/type-placement.md; judged backlog (2026-07):
|
|
docs/internal/plans/2026-07-02-type-dedup-backlog.md.
|
|
|
|
Usage: python3 scripts/check-type-duplicates.py [--jaccard 0.6] [--min-items 3]
|
|
"""
|
|
import argparse
|
|
import itertools
|
|
import re
|
|
from pathlib import Path
|
|
|
|
DEF_RE = re.compile(r'^pub (struct|enum) ([A-Za-z0-9_]+)(?:<[^>]*>)?\s*\{', re.M)
|
|
FIELD_RE = re.compile(r'(?:pub(?:\([^)]*\))?\s+)?([a-z_][a-z0-9_]*)\s*:\s*([^,\n]+)')
|
|
VARIANT_RE = re.compile(r'^\s{4}([A-Z][A-Za-z0-9_]*)', re.M)
|
|
KEYWORDS = {'where', 'if', 'let', 'match'}
|
|
|
|
|
|
def body_of(text, start):
|
|
i = text.index('{', start)
|
|
depth, j = 0, i
|
|
while j < len(text):
|
|
if text[j] == '{':
|
|
depth += 1
|
|
elif text[j] == '}':
|
|
depth -= 1
|
|
if depth == 0:
|
|
break
|
|
j += 1
|
|
return text[i + 1:j]
|
|
|
|
|
|
def collect(min_items):
|
|
types = []
|
|
for src in itertools.chain(
|
|
Path('crates').glob('*/*/src'),
|
|
Path('crates/extensions/packages').glob('*/src')):
|
|
crate = src.parent.name.removeprefix('ironclaw_')
|
|
for f in src.rglob('*.rs'):
|
|
try:
|
|
text = f.read_text(errors='ignore')
|
|
except OSError:
|
|
continue
|
|
for m in DEF_RE.finditer(text):
|
|
kind, name = m.group(1), m.group(2)
|
|
body = body_of(text, m.start())
|
|
if kind == 'struct':
|
|
items = frozenset(
|
|
(fm.group(1), re.sub(r'\s+', '', fm.group(2)).rstrip(','))
|
|
for fm in FIELD_RE.finditer(body)
|
|
if fm.group(1) not in KEYWORDS)
|
|
else:
|
|
items = frozenset((vm.group(1), '') for vm in VARIANT_RE.finditer(body))
|
|
if len(items) >= min_items:
|
|
types.append((crate, kind, name, items, f))
|
|
return types
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument('--jaccard', type=float, default=0.6)
|
|
ap.add_argument('--min-items', type=int, default=3)
|
|
args = ap.parse_args()
|
|
|
|
types = collect(args.min_items)
|
|
found = 0
|
|
for a, b in itertools.combinations(types, 2):
|
|
if a[0] == b[0] or a[1] != b[1]:
|
|
continue
|
|
i1, i2 = a[3], b[3]
|
|
union = len(i1 | i2)
|
|
jac = len(i1 & i2) / union if union else 0
|
|
n1 = frozenset(x[0] for x in i1)
|
|
n2 = frozenset(x[0] for x in i2)
|
|
njac = len(n1 & n2) / len(n1 | n2) if (n1 | n2) else 0
|
|
if jac >= args.jaccard or (njac >= 0.75 and min(len(n1), len(n2)) >= 4):
|
|
found += 1
|
|
print(f"jac={jac:.2f} njac={njac:.2f} {a[1]:6} "
|
|
f"{a[0]}::{a[2]} <-> {b[0]}::{b[2]}")
|
|
print(f"\n{found} candidate pair(s) from {len(types)} types "
|
|
f"(>= {args.min_items} items). Judge before acting.")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|