doc: centralize contributor workflows and enforcement knowledge - #1153
Merged
levy merged 27 commits intoSep 2, 2026
Conversation
The generated seal index understates the existing architecture rule count, so the canonical seal gate fails on the unchanged master tree. Refresh that count before adding new document units so every later commit can validate against an accurate index.
Broad test runs can pass without exercising the changed contract, and stale libraries can make local results misleading. Define explicitly filtered evidence and record the build and run context so reviewers can reproduce validation and identify coverage gaps.
Recorded expectations can hide regressions when regenerated as a side effect of a source change. Require approval of the exact affected baselines and rationale before regeneration, then retain provenance in the focused validation and commit record.
Simulation outcomes are easy to overstate when observation boundaries, independent repetitions, effective feature gates, or capture representation are implicit. Add canonical diagnosis and result-analysis procedures so causal and statistical claims carry reproducible evidence at the boundary they actually establish.
A derived module can handle a later initialization stage without ever being called when its effective stage count remains inherited from a base class. Make the count requirement explicit so lifecycle reviews compare every handled stage across the inheritance chain.
The existing include-direction check leaves application transport boundaries and common nondeterministic source patterns to manual discovery. Extend the canonical gate with normalized scope handling and focused tests so these candidates are reported consistently without scanning unrelated subtrees.
Path checks and clang-tidy cannot validate declaration-level NED and message names. Add a canonical checker that reads working-tree or staged changes precisely, checks complete added and renamed files, and integrates with the existing naming gate.
Document seal checks do not resolve changed source paths against the canonical seal registry, including generated message siblings and rename sources. Add a dedicated guard with explicit working-tree, staged, and path modes so sealed source changes stop before implementation.
Treating non-applicable checks as passes and proposing ledger rows for defects that should be fixed obscures the review outcome. Separate N/A results, require an explicit ledger disposition, and add the determinism cases that still require semantic judgment.
The enforcement inventory must distinguish path naming, declaration naming, document seals, and source seals, and it must define how diff-aware gates are invoked. Record the expanded gate surface, exit behavior, and focused self-test command in the canonical project documentation.
Repository instructions should point contributors and agents to canonical project documents instead of repeating mutable policy. Reduce the root instruction file to task-oriented routing so requirements, evidence procedures, seals, and review rules remain single-sourced.
Comment on lines
+44
to
+46
| if [ ${#SEALED_PATTERNS[@]} -eq 0 ]; then | ||
| echo "info: No sealed paths found in $STATUS_FILE. All files unsealed." | ||
| exit 0 |
There was a problem hiding this comment.
🟡 Empty registry bypasses invocation errors
With no source seals, SEALED_PATTERNS exits before argument validation. Unknown options and invalid paths then pass, misleading automation.
Prompt for agents
In doc/project/enforcement/check-source-seals.sh, validate the selected mode and all explicit arguments before treating an empty sealed-path list as a successful no-op. Preserve exit status 2 for unknown options and invalid paths even when the canonical registry currently contains no source seals. Add a regression test to test_check_source_seals.py using an empty Sealed paths section.
Was this helpful? React with 👍 or 👎 to provide feedback.
Comment on lines
+517
to
+521
| if selected_lines is None or "file-type" in structural_rules: | ||
| names = {name for _, _, name in declarations} | ||
| if path.stem not in names: | ||
| line = declarations[0][0] if declarations else 1 | ||
| add(findings, path, line, "NR-PKG", f"file stem '{path.stem}' must match a defined MSG type") |
There was a problem hiding this comment.
🟡 Secondary types mask wrong filenames
When any secondary type matches the filename, check_msg accepts a differently named primary type. The equivalent NED check does too.
Prompt for agents
The NR-PKG rule requires each NED or MSG file to be named after its primary type, but check_msg and check_ned only test whether the file stem appears anywhere in the set of declarations. Define the parser's primary-type rule for both formats and compare the stem against that declaration, while preserving support for legitimate secondary helper types and package.ned. Add tests where a later secondary type matches the filename but the primary type does not.
Was this helpful? React with 👍 or 👎 to provide feedback.
The naming wrapper previously invoked its declaration checker only for the exact src/inet scope and always used the working-tree diff. A clean topic branch therefore checked zero committed NED/MSG declarations, while a subsystem audit skipped declarations throughout its requested subtree. Give the checker a merge-base mode that reads the committed HEAD tree, and make explicit directories complete recursive scans. Route the wrapper to the branch or audit mode requested by the canonical workflow while preserving working-tree, staged, explicit-file, added-line, and structural-deletion behavior. Validate scopes before scanning so a focused audit cannot escape src/inet or include sibling subtrees. Add helper and wrapper regressions for committed-content isolation, recursive scope coverage, default working-tree behavior, structural deletions, added-line filtering, and invalid inputs. Update the before-push and subsystem-audit commands to select the corresponding modes. Validation: python3 -m unittest discover -v -s doc/project/enforcement -p test_*.py (48 passed); bash -n doc/project/enforcement/check-naming.sh; python3 -m py_compile doc/project/enforcement/check-ned-msg-naming.py doc/project/enforcement/test_check_naming.py; doc/project/enforcement/check-seals.sh; git diff --check.
Quoted defaults ending in an even run of backslashes left following comments visible to declaration checks. The scanner treated every quote preceded by a backslash as escaped, even when the final backslash was itself escaped, so comment examples could become naming candidates. Determine quote escaping from the parity of the complete preceding backslash run. Cover NED property-like comments, MSG declaration-like comments, escaped quotes, and comment markers that belong inside strings. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p test_check_naming.py (37 passed) - python3 -m py_compile doc/project/enforcement/check-ned-msg-naming.py doc/project/enforcement/test_check_naming.py - git diff --check
Directory-name exclusions left visualizer, third-party, and external subtrees outside the mechanical ambient-randomness and wall-clock check. Behavioral code added under those names would therefore rely on later semantic review even when the forbidden call was statically visible. Apply the source-pattern scan uniformly to every C++ file in the requested scope. Keep intentional diagnostic exceptions at exact code locations through the existing allowlist mechanism instead of exempting whole directory classes. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p test_check_architecture.py (8 passed) - python3 doc/project/enforcement/check-determinism.py src/inet - python3 -m py_compile doc/project/enforcement/check-determinism.py doc/project/enforcement/test_check_architecture.py - git diff --check
Working-tree and index modes leave clean committed topic branches unchecked, so the source-seal guard could not serve as a pull-request gate. Reading the branch-local registry would also let a branch hide a pre-existing seal by deleting its row alongside the source change. Compare committed source paths from the merge base through HEAD against the seal registry stored at that merge base. Preserve both endpoints of renames, retain the existing local modes, and run the checker regression suite and branch guard in a lightweight pull-request workflow. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p test_*.py (56 passed) - bash -n doc/project/enforcement/check-source-seals.sh - doc/project/enforcement/check-source-seals.sh --base origin/master - doc/project/enforcement/check-seals.sh - doc/project/enforcement/check-links.sh (47 files, 0 broken links) - git diff --check
The mandatory source-seal check had no trusted way to distinguish an unapproved edit from an explicitly authorized repair, so every sealed-source pull request remained permanently red. Declaration diff filtering also ignored a multiline MSG type when only its later definition delimiter changed, and NR-CI accepted rule-gate workflows without the check- prefix. Separate unprivileged checker tests from a pull_request_target sealing workflow that runs base-branch checker code over immutable base and head commits. Route sealed hits through a protected environment, bind approval to that exact range, cancel stale runs, and keep missing configuration fail closed. Record this contract as SR-PR-APPROVAL. Attribute multiline definitions to both the type-name and delimiter lines, and cover working-tree, staged, base, and non-activating comment changes. Enforce the build-*, *-tests, and check-* workflow forms and rename the workflows to match. The sealed-source-change environment and required-workflow ruleset still need repository-side configuration. Until they are configured, sealed changes fail closed. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p test_*.py (65 passed) - bash -n for the naming and source-seal checkers - python3 -m py_compile for the Python checker and tests - Ruby YAML load for both workflow files - doc/project/enforcement/check-seals.sh (18 documents, pass) - doc/project/enforcement/check-links.sh (47 files, 0 broken links) - doc/project/enforcement/check-source-seals.sh --diff (no source files) - doc/project/enforcement/check-naming.sh --base origin/master (NR-CI clean; only existing ledgered deviations reported) - git diff --check
Three project gates could disagree with the rules they enforce. MSG namespaces were not inspected, the determinism scanner required one exact spelling of std qualification while matching unrelated time and rand members, and the source-seal guard recognized only one Markdown cell layout. These gaps could admit naming, reproducibility, or seal violations, while also rejecting valid deterministic code. Validate MSG namespace declarations in full and diff-focused modes, including staged, base, rename, and structural-deletion cases. Tokenize the relevant C++ expressions so whitespace around scope operators is accepted while member and foreign namespace calls are excluded. Parse active seal rows as trimmed Markdown cells and return status 2 for malformed rows so source approval remains fail-closed. Add regression coverage for namespace policy and deletion-only changes, spaced C++ qualification and unrelated calls, and compact, expanded, commented, or malformed seal rows. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p "test_*.py" (80 tests passed) - python3 doc/project/enforcement/check-determinism.py src/inet - python3 doc/project/enforcement/check-ned-msg-naming.py --base origin/master - doc/project/enforcement/check-source-seals.sh --base origin/master - doc/project/enforcement/check-seals.sh - doc/project/enforcement/check-links.sh - git diff --check
The determinism gate rejected every std::chrono reference while recognizing random devices and host-clock reads only when they retained their fully qualified spelling. This both blocked deterministic duration calculations and allowed namespace imports or aliases to hide forbidden sources. Resolve using directives, namespace aliases, type aliases, typedefs, and alias chains within lexical scopes. Keep std::random_device forbidden at import, alias, and use sites, but report chrono only when system_clock, steady_clock, or high_resolution_clock calls now(). Preserve the existing rand(), time(), member-call, foreign-namespace, comment, literal, and ResultFilters behavior. OMNeT++ ends an MSG cplusplus block at the first literal }}. Document that boundary and cover it with a naming regression test so later declarations stay visible without adding C++ brace parsing that would disagree with the MSG compiler. Update the semantic checklist to describe host-clock reads rather than the entire chrono namespace. Validation: - python3 -m unittest discover -v -s doc/project/enforcement -p 'test_*.py' (85 tests passed) - python3 -m py_compile doc/project/enforcement/check-determinism.py doc/project/enforcement/check-ned-msg-naming.py doc/project/enforcement/test_check_architecture.py doc/project/enforcement/test_check_naming.py - python3 doc/project/enforcement/check-determinism.py src/inet - doc/project/enforcement/check-seals.sh - doc/project/enforcement/check-links.sh - git diff --check
The determinism gate resolved chrono clock aliases for static Clock::now()
calls, but object-style invocations such as Clock{}.now() and
std::chrono::system_clock().now() stopped at the constructed type. These
expressions still read host time and could pass the architecture gate.
Recognize empty brace and parenthesized construction after a resolved ambient
clock type, then walk the grouping parentheses that directly wrap the
construction before requiring a .now() call. Distinguish the outer
parenthesis of a surrounding call so consume((Clock{}).now()) is caught while
makeClock((Clock{})).now() remains excluded.
Keep ordinary member calls, foreign clock types, and deterministic chrono
temporaries excluded. Add focused direct and aliased regression cases for
brace, parenthesized, globally qualified, and redundantly grouped forms, plus
negative coverage for unrelated objects and factory results.
Validation:
- python3 -m unittest -v doc/project/enforcement/test_check_architecture.py
(17 tests passed)
- python3 -m unittest discover -v -s doc/project/enforcement -p 'test_*.py'
(87 tests passed)
- python3 -m py_compile doc/project/enforcement/check-determinism.py
doc/project/enforcement/test_check_architecture.py
- python3 doc/project/enforcement/check-determinism.py src/inet
- doc/project/enforcement/check-seals.sh
- doc/project/enforcement/check-links.sh
- git diff --check
The direct clock-object matcher treated every Clock{}.now() expression as a
runtime wall-clock read. Calls inside decltype, sizeof, and noexcept operands
are never evaluated, so valid type and compile-time queries caused the
architecture gate to report false determinism candidates.
Track the parenthesis depth introduced by these unevaluated operators and
suppress only object-style clock reads within that context. Also recognize
the valid unparenthesized sizeof Clock{}.now() form. Once the matching operand
closes, ordinary calls, conditional expressions, and constructions using a
decltype result continue to report ambient clock reads.
Add focused cases covering aliased and qualified clock objects, nested
grouping, all three unevaluated operators, unparenthesized sizeof, and
evaluated expressions adjacent to each operator.
Validation:
- python3 -m unittest -v doc/project/enforcement/test_check_architecture.py
(19 tests passed)
- python3 -m unittest discover -v -s doc/project/enforcement -p 'test_*.py'
(89 tests passed)
- python3 -m py_compile doc/project/enforcement/check-determinism.py
doc/project/enforcement/test_check_architecture.py
- python3 doc/project/enforcement/check-determinism.py src/inet
- doc/project/enforcement/check-seals.sh
- doc/project/enforcement/check-links.sh
- git diff --check
The determinism checker applied unevaluated-operand suppression only to constructed clock objects. Static Clock::now() calls therefore remained false findings in decltype, noexcept, and sizeof, and C++20 requires-expressions had no unevaluated scope at all. Conversely, lambda bodies inside decltype inherited suppression even though their call operators can execute later. Build a shared delimiter-aware evaluation map for both static and object-style clock detection. Parenthesized unevaluated operators and braced requirements temporarily disable reporting, while a deferred lambda body restores evaluated state until its matching brace closes. Scan decltype alias declarations far enough to reach such bodies, and distinguish parameterized requires-expressions from constrained function bodies. Add direct, aliased, and namespace-qualified coverage for static calls; constructed and static requirements with and without parameter lists; reads after a requires-expression; simple and trailing-return lambda bodies; nested unevaluated operations inside a lambda; and constrained function bodies. Validation: - python3 -m unittest -v doc/project/enforcement/test_check_architecture.py (24 tests passed) - python3 -m unittest discover -v -s doc/project/enforcement -p 'test_*.py' (94 tests passed) - python3 -m py_compile doc/project/enforcement/check-determinism.py doc/project/enforcement/test_check_architecture.py - python3 doc/project/enforcement/check-determinism.py src/inet - g++ -std=c++20 -fsyntax-only over representative clock-context forms - doc/project/enforcement/check-seals.sh - doc/project/enforcement/check-links.sh - git diff --check
NED parameter and gate declarations may split their type or direction, identifier, properties, and terminator across several lines. The line-oriented checker missed nonconforming identifiers in those declarations and could not associate edits outside the identifier line with the declaration they changed. Assemble lexical statements inside parameters and gates sections while respecting strings and nested delimiters. Track the physical token-bearing lines for each statement so explicit scans check everything, diff modes activate a declaration from any contributing changed line, and unrelated legacy declarations remain silent. Cover parameter and gate failures in explicit, working-tree, staged-index, and committed-base modes, including index/HEAD content isolation and terminator-only changes.
Calls to rand() and time() inside decltype, sizeof, noexcept, and requires expressions never execute. Their detection path did not consult the evaluation-context map, so deterministic source could fail the architecture gate even though no ambient state was read. Apply the same unevaluated-operand classification already used for chrono clock reads before reporting global or std-qualified C calls. Retain findings for evaluated calls and verify that detection resumes immediately after a requires-expression. Add regression coverage for unqualified, global-qualified, and std-qualified rand/time references across every supported unevaluated context.
An empty list of parsed seal rows previously looked the same as a missing or renamed canonical section, allowing an unavailable registry structure to pass as unsealed. Require both canonical boundary headings before accepting an empty section.
Comment stripping leaves blank placeholders to preserve line numbers. Treating those selected placeholders as statement changes surfaced unrelated legacy multiline fields in worktree, staged, and base modes. Only substantive post-comment content selects a buffered statement.
Fenced examples can contain canonical headings and active-looking seal rows. Treating them as registry content can hide renamed real sections or create false seals, so track Markdown fences before parsing visible headings and rows.
Removing HTML comments before recognizing fences can fabricate an opener or leak comment state from a valid fence info string. Inspect raw opener lines first so active seal rows remain enforceable and valid registries stay parseable.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Move reusable contributor, validation, simulation-analysis, and enforcement knowledge into the canonical INET project documentation under
doc/project/.This change:
AGENTS.mdto routing into canonical project policy.Motivation
Reusable contributor policy should live in
doc/project/, where developers, reviewers, and agents share one authoritative source. Keeping this guidance only in agent skills or duplicating it in instruction files makes it harder to discover, review, and maintain.The new enforcement scripts also turn mechanically identifiable requirements into canonical project gates while preserving semantic judgment for the review checklist.
Reading order
AGENTS.mdthrough canonical project policy.Each enforcement implementation lands with its directly related tests.
Main changes
Testing and contributor workflow
TR-FOCUSED-EVIDENCE.Simulation evidence
PcapRecorderobservation, protocol representation, checksum, and FCS configuration.Architecture and lifecycle
numInitStages()coverage for stages handled by a module or its base classes.Enforcement
PASS,N/A,FLAG, andQUESTIONreview outcomes.Architectural surface
This pull request changes project rules, contributor procedures, domain guidance, enforcement scripts, and documentation routing.
It does not change:
src/inet/;No architecture or naming exception rows are added.
Validation
Working directory:
No INET library build was required because the branch changes only documentation and standalone enforcement scripts.
Seal checks, link checks, and applicable enforcement tests also passed independently at every commit.
The final branch changes exactly the intended 23 paths, with byte-for-byte content and executable modes verified against the prepared tree.
Existing full-tree findings
The full naming gate returns status 1 for pre-existing paths represented by
NV-02,NV-03,NV-17, andNV-18.The full architecture gate returns status 1 for pre-existing source candidates. Existing common and visualizer findings correspond to current
AV-ORG-*andAV-VIS-*entries. The new socket-boundary check additionally exposes existing SCTP and RTP application dependencies that are not introduced by this pull request and are not currently ledgered.Baselines and seals
No behavioral baseline was changed, and no path or document seal status was changed.
The generated document-seal index was refreshed so every commit passes the canonical seal check.