Skip to content

docs(license): fill in the BSL 1.1 Parameters block - #2399

Closed
ooples wants to merge 4 commits into
masterfrom
chore/bsl-parameters-block
Closed

ooples wants to merge 4 commits into
masterfrom
chore/bsl-parameters-block

Conversation

@ooples

@ooples ooples commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

Licence housekeeping decided by the maintainer on 2026-10-28. No tracking issue.

Summary

LICENSE is the Business Source License 1.1, but it had no Parameters block and a blank Change Date, so it named no Licensor, Licensed Work, Additional Use Grant or Change License. Without those, the licence does not say who grants it, what it covers, or when it converts.

This fills them in with the terms published at https://aidotnet.dev/license. The one exception is the Licensor, which the maintainer has confirmed is CrowdTrainer LLC:

Parameter Value
Licensor CrowdTrainer LLC
Licensed Work AiDotNet (the "Software")
Additional Use Grant You may use the Licensed Work for any non-commercial purpose, including education, research, personal projects, and internal evaluation. Training and inference operations are unrestricted regardless of license status.
Change Date Four years from the date of each release
Change License Apache License 2.0
  • Apart from the Licensor, the values are the website's wording, copied word for word.
  • It also adds the standard BSL "alternative licensing" line (pointing to aidotnet.dev/license and licensing@aidotnet.dev) and the standard BSL Notice paragraph.
  • It fills in the blank Change Date: in the "Standard License Header" section with the same value.
  • In that section, "the LICENSE.TXT file" (twice) now reads "the LICENSE file", because the file is LICENSE (commit 4426782).
  • The BSL terms themselves are unchanged. This is a one-file change, so no build was run.

Maintainer decisions (2026-10-28) and what was done

  • Additional Use Grant: decided. It keeps the website's wording. The Community tier (free production use under US$1M revenue and fewer than 5 developers) stays a separate, registered licence tier, not part of the grant.
  • Licensor and copyright holder: decided, CrowdTrainer LLC. Places changed:
    • LICENSE: Licensor is CrowdTrainer LLC. The standard header's Copyright (C) 2026 Franklin Moormann is now Copyright (C) 2024-2026 CrowdTrainer LLC.
    • NOTICE: Copyright 2024 Ooples Finance is now Copyright (C) 2024-2026 CrowdTrainer LLC. "software developed at Ooples Finance" and "private projects maintained by Ooples Finance" now name CrowdTrainer LLC.
    • <Company>Ooples Finance</Company> and <Copyright>Ooples Finance LLC 2023</Copyright> are now CrowdTrainer LLC and Copyright (C) 2024-2026 CrowdTrainer LLC in 7 projects:
      • src/AiDotNet.csproj
      • AiDotNet.Privacy.HE
      • AiDotNet.Storage.Elasticsearch, .Neo4j, .Postgres, .Redis and .Sqlite
    • The 7 projects were edited by an anchored script. A dry run found 14 matches, as expected, and every hunk was read.
    • The website, which lives in this repo under website/: the Licensor on website/src/pages/license.astro and the footer copyright in website/src/components/Footer.astro. The live https://aidotnet.dev/license keeps saying Ooples Finance LLC until this PR is merged and the site is redeployed.
    • <Authors> is authorship and is unchanged.
  • Change Date wording: "Four years from the date of each release" is the website's phrase. The BSL terms already say a version converts on the fourth anniversary of its first public distribution, so the two agree.

Follow-up decisions (2026-10-28) and what was done

  • Years: decided, 2024-2026. LICENSE, NOTICE and all 7 package <Copyright> values read Copyright (C) 2024-2026 CrowdTrainer LLC.
  • Licence issuer: decided, CrowdTrainer LLC. website/src/pages/enterprise.astro now says "a valid CrowdTrainer LLC-issued enterprise license" and "a CrowdTrainer LLC license server".
  • Contracting entity: decided, CrowdTrainer LLC. website/src/pages/federal-use.astro now reads "CrowdTrainer LLC is a small-business contractor (single-person LLC)."
  • No other issuer uses: no other "Ooples" in this repo refers to the licence issuer. What remains is the GitHub org (URLs, CODEOWNERS, release owner) and non-licensing code comments.
  • Per-file headers (count only, not rewritten): 30 files carry Copyright (c) AiDotNet. All rights reserved.. 27 use //, which the maintainer's pattern matches, and 3 are .ps1 files with #. None use the (c) 2024 form.
  • No build was run: the changes are licence text, NOTICE, .csproj metadata (checked to parse as XML) and website strings.

Summary by CodeRabbit

  • Documentation
    • Updated the license terms to identify the licensor and covered work, clarify permitted non-commercial uses, and state that training and inference are unrestricted regardless of license status.
    • The license specifies a change to Apache License 2.0 four years after each release, provides licensing contact information, and clarifies the work’s open-source status and future licensing.
  • Chores
    • Updated company and copyright attribution across package information and website pages to CrowdTrainer LLC.

The LICENSE carried the BSL 1.1 terms with no Parameters block and a blank
Change Date, so it named no Licensor, Licensed Work, Additional Use Grant or
Change License. Fill them in with the terms published at
https://aidotnet.dev/license.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated
aidotnet_website Ignored Ignored Preview Oct 28, 2026 6:57pm UTC
aidotnet-playground-api Ignored Ignored Preview Oct 28, 2026 6:57pm UTC

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 25 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 2127bc5 and 4426782.

Files selected for processing: 11
  • LICENSE
  • NOTICE
  • src/AiDotNet.Privacy.HE/AiDotNet.Privacy.HE.csproj
  • src/AiDotNet.Storage.Elasticsearch/AiDotNet.Storage.Elasticsearch.csproj
  • src/AiDotNet.Storage.Neo4j/AiDotNet.Storage.Neo4j.csproj
  • src/AiDotNet.Storage.Postgres/AiDotNet.Storage.Postgres.csproj
  • src/AiDotNet.Storage.Redis/AiDotNet.Storage.Redis.csproj
  • src/AiDotNet.Storage.Sqlite/AiDotNet.Storage.Sqlite.csproj
  • src/AiDotNet.csproj
  • website/src/pages/enterprise.astro
  • website/src/pages/federal-use.astro

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 27c4b75 and 2127bc5.

Files selected for processing: 11
  • LICENSE
  • NOTICE
  • src/AiDotNet.Privacy.HE/AiDotNet.Privacy.HE.csproj
  • src/AiDotNet.Storage.Elasticsearch/AiDotNet.Storage.Elasticsearch.csproj
  • src/AiDotNet.Storage.Neo4j/AiDotNet.Storage.Neo4j.csproj
  • src/AiDotNet.Storage.Postgres/AiDotNet.Storage.Postgres.csproj
  • src/AiDotNet.Storage.Redis/AiDotNet.Storage.Redis.csproj
  • src/AiDotNet.Storage.Sqlite/AiDotNet.Storage.Sqlite.csproj
  • src/AiDotNet.csproj
  • website/src/components/Footer.astro
  • website/src/pages/license.astro

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The LICENSE now identifies CrowdTrainer LLC as licensor and AiDotNet as the licensed work. It adds use terms, licensing details, and notices about the license and future open-source availability. It sets the change date to four years from each release.

Changes

License and attribution

Layer / File(s) Summary
License terms and change date
LICENSE
The LICENSE adds non-commercial use terms, allows training and inference regardless of license status, names Apache License 2.0 as the change license, and provides licensing contact details. It also states that the Business Source License is not an open-source license and sets the change date to four years from each release.
Organization attribution updates
NOTICE, src/*.csproj, website/src/components/Footer.astro, website/src/pages/license.astro
The NOTICE, project metadata, and website text now name CrowdTrainer LLC. The author values remain unchanged where specified.

Priority: Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Other

Merge Risk: Minimal · previous review 2127b

The updated license states the usage grant and four-year transition. No concrete merge-blocking issue was established.

Pre-merge checks: Passed 4
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title accurately describes the license-related changes, but it does not mention the broader CrowdTrainer LLC ownership and metadata updates across the repository.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A license sets its terms in place
New names appear across the space
The change date waits four years to pass
Project credits align at last
Clear notices mark the page

Comment @coderabbitai help to get the list of available commands.

The maintainer confirmed CrowdTrainer LLC is the licensing entity. LICENSE (the
Licensor parameter and the standard header's copyright line), NOTICE, every
package's Company/Copyright metadata, and the website's licence page and
footer now name it instead of Ooples Finance LLC or Franklin Moormann.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The copyright lines in LICENSE, NOTICE and the package metadata now read
2024-2026. The website's enterprise and federal-use pages name CrowdTrainer LLC
as the licence issuer and contracting entity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ists

The header named LICENSE.TXT, a file this repository doesn't have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ooples

ooples commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

Folded into #2412 with git merge (all commits kept) so these changes share one 2-hour CI run. #2412 is now 100 files. Push further work for this change to #2412's branch.

@ooples ooples closed this Oct 30, 2026
ooples added a commit that referenced this pull request Oct 30, 2026
Part of the CrowdTrainer LLC licensing change (#2399).

## What this does

This rewrites the per-file header `Copyright (c) AiDotNet. All rights
reserved.` to `Copyright (c) CrowdTrainer LLC. All rights reserved.` in
all **30 files** that carry it:

- 27 files with the `//` comment form:
  - `src/NeuralNetworks/Generation/` (3)
  - `src/Training/` (1)
  - `tests/AiDotNet.Serving.Tests/IntegrationTests/` (5)
  - `tests/AiDotNet.Tests/` (17)
  - `tools/ModelPerfFixtureRunner/` (1)
- 3 PowerShell scripts in `tools/ModelPerfProbe/` with the `#` form,
which keep `#`.

No file uses the `(c) 2024 AiDotNet` variant. This matches the
maintainer's decision (2026-10-28) that CrowdTrainer LLC is the
copyright holder.

## How the edit was made (CLAUDE.md scripted-bulk rules)

- **Starting point:** a clean tree off `master` at d42d6b6.
- **Anchored whole-line match:** `^(//|#) Copyright \(c\) (2024
)?AiDotNet\. All rights reserved\.\r?$`, with a UTF-8 BOM allowed only
on line 1.
- **Byte-level edit:** each file keeps its BOM (none of these have one)
and its CRLF line endings.
- **Dry run first:** 30 matches, exactly one per file. These are the
only tracked files that contain the text.
- **Diff checked hunk by hunk:** a checker confirmed every hunk is a
one-line, in-place swap of the old header for the new one, with the
comment marker and BOM unchanged and the changed files exactly matching
the list. It found 30 files, 30 hunks and 0 problems.
- **Build:** `src/AiDotNet.csproj` net10.0 built through `build.ps1`
with 0 errors.
- No tests were run locally, by rule.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Documentation**
* Updated copyright attribution across project source files, tests, and
tools. No application behavior or test logic changed.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ooples added a commit that referenced this pull request Nov 4, 2026
…combined) (#2412)

**Combined PR (100 files):** to save 2-hour CI runs, this PR carries 3
independent changes, merged with `git merge` so every original commit is
kept. Each section below is the original description.

- this PR: fix: route activation caches through SaveForBackward (sweep
3/3)
- #2399 (closed, merged in here): docs(license): fill in the BSL 1.1
Parameters block
- #2420 (closed, merged in here): feat(analyzers): ADN0065 becomes an
error with a [KeepsActivation] exemption, training guards and a baseline
ratchet

# This PR: fix: route activation caches through SaveForBackward (sweep
3/3)

Part of #2391

## The class
Layers and models stored per-forward activations in fields (`_lastInput
= input;`, `_xOutput = ...`, `_cache.Add(...)`) unconditionally. So in
inference every activation stayed reachable after the forward, and peak
memory was O(sum of activations). This is the third of three sweeps of
the ADN0065 sites (`ActivationCacheGuardAnalyzer`), following #2408's
pattern.

## What changed, per site
- **Fields that nothing reads (the large majority):** removed. The value
stays a local. Most of these layers already run their backward on the
tape, so the field was write-only. This covers the 11 SSM layers (PaTH,
RWKV, RWKV-7, RG-LRU, Rebased, RetNet, Rodimus, S4D, S5, TTT,
TransNormerLLM) and
SelfAttention/Separable/SoftTree/SpatialTransformer/SpectralNorm/Spiking/SpiralConv/Split/SpyNet/SE/Subpixel/Swin
(including its `[windows, area, 3*dim]`
qkv)/TemporalMemory/TimeDistributed/TimeEmbedding/TransformerDecoder/Upsample/Upsample3D/UNetDiscriminator's
down block. It also covers TNet, GaussianSplatting, the TimeSeries
private layers (DeepANT, whose pre-activation buffer was never read;
LSTM-VAE decoder; Chronos; N-HiTS) and every tabular backbone/head cache
(AutoInt, FT-Transformer, GANDALF, Mambular, NODE, SAINT, TabDPT, TabM,
TabPFN, TabR, TabTransformer, FeatureTokenizer, GhostBatchNorm,
ContextEncoder). The list accumulators that nothing read went too.
- **Read by a manual backward:** these go through `SaveForBackward`:
SparseLinear, UNetDiscriminator's up block, and Rebased q/k/v.
- **Models and non-layer components:** `NeuralNetworkBase` is in flight
elsewhere, so I did not add the helper there. The new
`ManualBackwardCache<T>` (`src/NeuralNetworks/ManualBackwardCache.cs`)
applies the same rule as `LayerBase.SaveForBackward`: no `InferenceMode`
scope, no recording tape unless `KeepActivationCacheUnderTape`, plus the
train/eval flag where the caller has one. GraphClassificationModel,
AutoInt, DuelingNetwork and the prototype `SimpleNeuralNetwork` use it.
AutoInt now keeps the forward batch size separately, so its
learning-rate scaling does not depend on whether the caches were kept.
- **Read only by an auxiliary loss or a learning rule:** SelfAttention
attention scores (`[B,H,S,S]`), SpatialTransformer theta, SE excitation
weights, and SynapticPlasticity's STDP inputs. These are now kept only
in a training pass outside `InferenceMode` (and, for the aux-loss ones,
only with `UseAuxiliaryLoss` on). `ShouldCacheForBackward` is the wrong
guard here because it is false under a recording tape, which is exactly
when those consumers run. The ADN0065 analyzer from #2420, merged in
here, recognises this `IsTrainingMode && !InferenceMode.IsActive` guard,
so these sites are no longer reported and none of them is in the
baseline.
- **PointNet / DGCNN / PointNet++:** they wrote every layer's input and
output into `NeuralNetworkBase._layerInputs` / `_layerOutputs`. Nothing
reads those dictionaries after these models' forward:
`ForwardWithFeatures` repopulates its own, and `_layerInputs` is never
read anywhere. The writes are removed.

## ADN0065 in this batch's files (net10.0 SARIF)
| | count |
|---|---|
| master | 299 (4 of them in the excluded `NeuralNetworkBase.cs`) |
| this PR | 21 (the same 4 in `NeuralNetworkBase.cs`, plus the 17 below)
|

_The table above was measured before #2420 was merged in. With the
merged analyzer, the four aux-loss and learning-rule sites are no longer
reported, and the authoritative count is the baseline itself: 566
entries after review._

## Sites left alone, and why
- Cross-call state:
  - `RVM._hiddenState` (recurrent matting memory between frames).
- `LinkPredictionModel._nodeEmbeddings` (read by `PredictEdges` after
the forward).
  - `VariationalAutoencoder._pendingKlLoss` (consumed by the loss).
  - `BayesianDenseLayer._samplePending` (a bool flag).
- `SpatialPoolerLayer._lastBinaryOutput` (Hebbian learning state,
`[ColumnCount]`, the same data as `LastOutput`).
- Read by public accessors:
- PointNet/DGCNN/PointNet++ `_globalFeatures` (`ExtractGlobalFeatures`).
- `SetAbstractionLayer._lastCentroidPositions` (segmentation
upsampling).
  - `TabRBase._neighborIndicesCache` (`GetRetrievedNeighborIndices`).
- `RWKV7Block._incomingVFirst`: passes v_first to the time-mix within
the same forward. It is one tensor shared by every block, so it does not
grow with depth.
- `SelfAttentionLayer._sdpaOutScratch`: an intentionally reused output
buffer.
- Gated, but by a guard the analyzer does not recognise: the four
aux-loss and learning-rule sites listed above.
- `NeuralNetworkBase.cs` (4 sites): excluded, in flight elsewhere. Its
`_layerInputs` writes (2003/2034/2086) have no reader anywhere. Its
`_layerOutputs` writes are read only by CapsuleNetwork and
ResidualNeuralNetwork.

## Not covered
The tabular models, DuelingNetwork and the prototype have no train/eval
flag, and their callers do not enter `InferenceMode`. Their caches now
follow the central policy, but the memory win needs callers to predict
inside an `InferenceMode` scope.

## Verification
- `src/AiDotNet.csproj` builds on net10.0 and net471 with 0 errors.
- ADN0065 counts come from the net10.0 build's SARIF (`-p:ErrorLog`).
- No tests were run locally; CI runs them.

# From #2399: docs(license): fill in the BSL 1.1 Parameters block

Licence housekeeping decided by the maintainer on 2026-10-28. No
tracking issue.

## Summary

`LICENSE` is the Business Source License 1.1, but it had no Parameters
block and a blank Change Date, so it named no Licensor, Licensed Work,
Additional Use Grant or Change License. Without those, the licence does
not say who grants it, what it covers, or when it converts.

This fills them in with the terms published at
https://aidotnet.dev/license. The one exception is the Licensor, which
the maintainer has confirmed is CrowdTrainer LLC:

| Parameter | Value |
|---|---|
| Licensor | CrowdTrainer LLC |
| Licensed Work | AiDotNet (the "Software") |
| Additional Use Grant | You may use the Licensed Work for any
non-commercial purpose, including education, research, personal
projects, and internal evaluation. Training and inference operations are
unrestricted regardless of license status. |
| Change Date | Four years from the date of each release |
| Change License | Apache License 2.0 |

- Apart from the Licensor, the values are the website's wording, copied
word for word.
- It also adds the standard BSL "alternative licensing" line (pointing
to aidotnet.dev/license and licensing@aidotnet.dev) and the standard BSL
Notice paragraph.
- It fills in the blank `Change Date:` in the "Standard License Header"
section with the same value.
- In that section, "the LICENSE.TXT file" (twice) now reads "the LICENSE
file", because the file is `LICENSE` (commit 4426782).
- The BSL terms themselves are unchanged. This is a one-file change, so
no build was run.

## Maintainer decisions (2026-10-28) and what was done

- **Additional Use Grant: decided.** It keeps the website's wording. The
Community tier (free production use under US$1M revenue and fewer than 5
developers) stays a separate, registered licence tier, not part of the
grant.
- **Licensor and copyright holder: decided, CrowdTrainer LLC.** Places
changed:
- `LICENSE`: Licensor is CrowdTrainer LLC. The standard header's
`Copyright (C) 2026 Franklin Moormann` is now `Copyright (C) 2024-2026
CrowdTrainer LLC`.
- `NOTICE`: `Copyright 2024 Ooples Finance` is now `Copyright (C)
2024-2026 CrowdTrainer LLC`. "software developed at Ooples Finance" and
"private projects maintained by Ooples Finance" now name CrowdTrainer
LLC.
- `<Company>Ooples Finance</Company>` and `<Copyright>Ooples Finance LLC
2023</Copyright>` are now `CrowdTrainer LLC` and `Copyright (C)
2024-2026 CrowdTrainer LLC` in 7 projects:
    - `src/AiDotNet.csproj`
    - `AiDotNet.Privacy.HE`
- `AiDotNet.Storage.Elasticsearch`, `.Neo4j`, `.Postgres`, `.Redis` and
`.Sqlite`
- The 7 projects were edited by an anchored script. A dry run found 14
matches, as expected, and every hunk was read.
- The website, which lives in this repo under `website/`: the Licensor
on `website/src/pages/license.astro` and the footer copyright in
`website/src/components/Footer.astro`. **The live
https://aidotnet.dev/license keeps saying Ooples Finance LLC until this
PR is merged and the site is redeployed.**
  - `<Authors>` is authorship and is unchanged.
- **Change Date wording:** "Four years from the date of each release" is
the website's phrase. The BSL terms already say a version converts on
the fourth anniversary of its first public distribution, so the two
agree.

## Follow-up decisions (2026-10-28) and what was done

- **Years: decided, 2024-2026.** LICENSE, NOTICE and all 7 package
`<Copyright>` values read `Copyright (C) 2024-2026 CrowdTrainer LLC`.
- **Licence issuer: decided, CrowdTrainer LLC.**
`website/src/pages/enterprise.astro` now says "a valid CrowdTrainer
LLC-issued enterprise license" and "a CrowdTrainer LLC license server".
- **Contracting entity: decided, CrowdTrainer LLC.**
`website/src/pages/federal-use.astro` now reads "CrowdTrainer LLC is a
small-business contractor (single-person LLC)."
- **No other issuer uses:** no other "Ooples" in this repo refers to the
licence issuer. What remains is the GitHub org (URLs, CODEOWNERS,
release owner) and non-licensing code comments.
- **Per-file headers (count only, not rewritten):** 30 files carry
`Copyright (c) AiDotNet. All rights reserved.`. 27 use `//`, which the
maintainer's pattern matches, and 3 are `.ps1` files with `#`. None use
the `(c) 2024` form.
- **No build was run:** the changes are licence text, NOTICE, `.csproj`
metadata (checked to parse as XML) and website strings.

## Summary by CodeRabbit

* **Documentation**
* Updated the license terms to identify the licensor and covered work,
clarify permitted non-commercial uses, and state that training and
inference are unrestricted regardless of license status.
* The license specifies a change to Apache License 2.0 four years after
each release, provides licensing contact information, and clarifies the
work’s open-source status and future licensing.
* **Chores**
* Updated company and copyright attribution across package information
and website pages to CrowdTrainer LLC.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

# From #2420: feat(analyzers): ADN0065 becomes an error with a
[KeepsActivation] exemption, training guards and a baseline ratchet

Part of #2391

## What this does

ADN0065 (`ActivationCacheGuardAnalyzer`) reports a backward-activation
cache written in a `Forward*` body without a training-pass guard. An
ungated write keeps the activation reachable after the pass, so peak
inference memory becomes O(sum of activations) instead of O(max live
set). Until now the rule was `Info`, so nothing stopped new ungated
caches. This PR makes it an `Error` with a ratchet, and tightens what it
recognises so the error has no known false positives.

### 1. `[KeepsActivation("reason")]`
`src/Attributes/KeepsActivationAttribute.cs`. A field attribute that
marks a deliberate keep, such as recurrent state carried between frames
or a value a later public call reads. The reason is required:
- The analyzer reports a blank, whitespace or `null` reason as
**ADN0068** (Error), and that field gets no exemption, so ADN0065 still
fires on it.
- The constructor throws on a blank reason.

### 2. Guards ADN0065 now recognises
- The `SaveForBackward(ref _field, value)` call is not a raw write, so
it is never reported.
- `if (ShouldCacheForBackward)` and the older
`ShouldCacheActivationsForManualBackward`, either directly or through a
local initialised from one (`bool cacheBwd = ShouldCacheForBackward; if
(cacheBwd) ...`). Only the **then**-branch counts: a write in the `else`
of a guard runs exactly when no backward is coming, so it is reported.
- The ternary form `_x = guard ? value : null`. If both branches store
something, it is still reported.
- The aux-loss / learning-rule training guard. An `&&` chain that both
requires `IsTrainingMode` and excludes `!InferenceMode.IsActive` counts
as a guard, for example `UseAuxiliaryLoss && IsTrainingMode &&
!InferenceMode.IsActive`. `ShouldCacheForBackward` is false under a
recording tape, which is exactly when those consumers run. Either half
on its own, or the two joined with `||`, is still reported.

### 3. What ADN0065 now detects
- Fields named `*Out`, in addition to `_last*`, `*Output`, `*Outputs`
and `[Scratch]`.
- `.Add(x)` into a collection field (`ICollection<T>`), which retains
the value just as an assignment does. `Tensor<T>.Add` is arithmetic and
is not counted.
- Value-typed and type-parameter-typed fields are skipped, because they
cannot pin a tensor.
- Null and `default` clears are skipped, as before.

### 4. Error plus baseline ratchet
This reuses the `ModelDefectBaselines` mechanism (ADNDEF001-003 style):
- `ModelDefectBaselines.UnguardedActivationCaches` lists the violating
fields, keyed `Containing.Type.field`.
- A field outside the set fails the build with ADN0065.
- A listed field that no longer violates is **ADNDEF004** ("baseline
entry is stale; remove its line"). So when a count falls, the build
tells you to lower the baseline, and the set can only shrink.
- The rule runs only on the `AiDotNet` assembly. Test and tool
assemblies define throwaway layers.
- The message now names the containing type, so a reported site maps
straight to its baseline key.

## Baseline: 820 fields, from origin/master `8542dd47e`

- **How it was measured.** The commit that adds the baseline records the
method: build `src/AiDotNet.csproj` for net10.0 at `8542dd47e` with this
analyzer and an empty baseline, then read the ADN0065 results from the
SARIF error log. That gave 898 write sites across 820 distinct fields,
and those 820 keys are the set. The set has 820 distinct entries and no
duplicates. I did not keep that SARIF and have not re-measured it.
- **How it is verified.** This branch changes nothing under `src/`
except the analyzer, the baseline and the new attribute, so `src` is
otherwise identical to `8542dd47e`. A 0-error `src` build at this head
therefore proves the set is exact: an unlisted violating field fails as
ADN0065, and a listed field that no longer violates fails as ADNDEF004.
**That local build has not completed.** The machine ran low on memory
and the build was stopped. CI's build of this PR is the verification. A
later commit narrows `IsGuarded` to the then-branch, which can only add
violations. If it adds any, CI lists them as ADN0065, and they get added
to the set.

**Interaction with the activation-cache sweeps #2412, #2413 and #2416.**
Each of those PRs gates fields that are listed here. Whichever lands
second has to delete the lines for the fields it fixed. ADNDEF004 names
each of those lines, so this is mechanical.

## Tests
(`tests/AiDotNet.Tests/Generators/ActivationCacheGuardAnalyzerTests.cs`)
and the mutation each catches

| Test | Analyzer mutation that makes it fail |
|---|---|
| `UnguardedField_IsReportedAsError` | `IsBackwardCacheField` returns
false, or the `ReportDiagnostic` call is removed |
| `KeepsActivationWithReason_SuppressesTheField` | the
`KeepsActivation(field) is { } keep && HasReason(keep)` skip in
`AnalyzeMethod` is removed |
| `KeepsActivationWithoutReason_IsReportedAndDoesNotExempt` (`""`, `"
"`, `null`) | `IsNullOrWhiteSpace` is removed from `HasReason` (ADN0065
disappears), or the `SymbolKind.Field` action is removed (ADN0068
disappears) |
| `SaveForBackward_IsNotReported` | any field passed by `ref` to an
invocation is treated as a write |
| `ShouldCacheForBackwardIf_IsNotReported` (direct, and through a local)
| `IsGuarded` returns false, or the local-initializer walk is removed |
| `WriteInElseBranchOfGuard_IsReported` | the `child !=
ifStatement.Statement` test is removed from `IsGuarded` |
| `ShouldCacheForBackwardTernary_IsNotReported` | the `IsGuardedValue`
check is removed |
| `TernaryThatStoresOnBothBranches_IsReported` | the
`IsNullLike(conditional.WhenFalse)` requirement is removed |
| `AuxiliaryLossTrainingGuard_IsNotReported` (4 forms) | the
`requiresTraining && excludesInference` return in `IsGuardCondition` is
removed |
| `IncompleteTrainingGuard_IsReported` (3 forms) | `IsTrainingMode`
alone is accepted, or conjuncts are collected through `\|\|` |
| `OutSuffixField_IsReportedUnlessGuarded` | the `EndsWith("Out")` name
test is removed |
| `AddIntoListField_IsReportedUnlessGuarded` | the
`InvocationExpressionSyntax` (`Add`) branch is removed |
| `TensorArithmeticAddOnCacheField_IsNotReported` | the `IsCollection`
filter on `Add` is removed |
| `ValueTypedCacheNamedField_IsNotReported` | the `IsValueType` /
`TypeParameter` filter is removed |
| `BaselinedField_IsNotReported_ButANewFieldIs` | the
`_baseline.Contains(key)` skip is removed. It also catches an over-eager
ratchet: recording the key only after that skip, or dropping the
`!violating.ContainsKey` filter, makes the baselined field look stale |
| `StaleBaselineEntry_IsReported` | the compilation-end action is
removed |
| `OtherAssemblies_AreNotAnalyzed` | the `AssemblyName` check is removed
|

I reasoned these mutations from the code. I did not run them: tests run
in CI only, and no test host was run locally.

## Follow-up (not in this PR)

A field kept only so that a test can read it after the forward is not a
deliberate keep. CrossAttention `_lastContext` is one example. These
fields should be **removed**, or the test should get what it needs
through the API. They should not be exempted with `[KeepsActivation]`,
and the attribute's remarks say so. This PR touches no layer files or
`NeuralNetworkBase.cs`, because other PRs own them.

## Verification

- `src/AiDotNet.Generators` builds with 0 errors, and this change adds
no warnings.
- **Not verified locally:**
- `src/AiDotNet.csproj`. The net10.0 build was stopped for low machine
memory, and the other TFMs were not attempted.
  - The test project: tests run in CI only.
- No baselined type is declared in a file that contains `#if`, so I do
not expect stale entries on the legacy TFMs (net471, net462,
netstandard2.0). No build has confirmed that.

## Merge order with the other activation-cache sweeps

The ADN0065 baseline in this PR is exact for this tree. The combined
build reported 255 stale entries, for fields sweep 3 fixed, and 2 new
ones (`ConvolutionalLayer<T>._lastInput`/`_lastOutput` in
`ForwardTraced`, reported once the else-branch guard fix landed). Both
are corrected in `81867831b`: 820 → 567 entries.

#2413 (sweep 1) and #2416 (sweep 2) fix fields that are still listed
here. Whichever lands after this PR has to delete those entries. The
build names each one as `ADNDEF004 '<Type>.<field>' ... no longer
violates`, so it is mechanical.

## After review (`72414674c`)
- **Test-project compile:** the `WriteInElseBranchOfGuard_IsReported`
test had a raw newline inside a regular string literal (CS1010). That
would have broken the test assembly; it now uses `
`.
- **Negated guards:** ADN0065 no longer accepts a negated guard as a
guard. `if (!ShouldCacheForBackward)`, `if
(!this.ShouldCacheForBackward)`, `== false` and `!(IsTrainingMode &&
ShouldCacheForBackward)` all store exactly when no backward is coming,
and they are now reported.
- An `||` alongside the guard still counts as guarded, because the
aux-loss gates in #2413 use that form.
- New tests: `NegatedGuard_IsReported` (4 cases) and
`GuardComparedWithTrue_IsNotReported`. The generators project builds
with 0 errors.
- **`ConvolutionalLayer`:** a stray `[Scratch]`, left by a deleted
field, had attached to `_optimizedVerifiedForShape` and made it
ADN0065-eligible. The attribute and its baseline key are removed,
leaving 566 entries.
- **Known false positive:** an early-return guard, `if
(!ShouldCacheForBackward) return y; _last = x;`, is still reported. No
site in `src` uses that shape today.

🤖 Generated with [Claude Code](https://claude.com/claude-code)




<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **New Features**
* TabDPT and TabPFN models now provide parameter retrieval and
restoration methods.
* **Behavior Changes**
* Attention and transformation details are retained only during eligible
training runs.
* Synaptic-plasticity learning state is no longer updated during
inference or non-training runs.
* **Refactor**
* Forward-state handling has been streamlined across neural-network,
tabular, point-cloud, and time-series models; core forward calculations
and outputs remain unchanged.
* **Documentation**
* Updated license terms to identify CrowdTrainer LLC, clarify permitted
non-commercial use, and specify a change to Apache License 2.0 four
years after release.
* **Chores**
* Updated company and copyright attribution across package information
and website pages.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

## Prediction boundaries

AutoInt regression/classification predictions now enter `InferenceMode`,
releasing manual-backward activation caches during public prediction
calls. Classification training calls the forward/softmax path directly
so it retains training caches.

Dueling DQN action selection, action-value queries, lazy initialization,
and Double DQN target evaluation now use its inference prediction path.
The current-state training forward still retains the caches required by
`UpdateWeights`.

Regression coverage checks output parity, released caches, restored
scope after exceptions, training cache retention, and Dueling parameter
updates for terminal and nonterminal replay samples.

Validation: the net10.0 core build succeeded (analyzers/documentation
disabled for local memory limits); fifteen focused tests passed. Full CI
remains pending behind #2400. AutoInt training now computes and applies
tape gradients to the embeddings, attention stack, MLP and head; the
cache regression exercises the real training path.



## AutoInt training

Both legacy AutoInt training APIs now compute a differentiable batch
objective and apply SGD to the complete parameter set. Numerical and
categorical embeddings, reshapes and attention stay on the tape.
Attention splits heads independently. Gradient buffers are scratch state
rather than published parameters, and six obsolete activation-cache
baseline entries are removed.

Fifteen focused tests pass, including finite differences for every
parameter for classification/regression with and without categorical
features, categorical-only training, independent attention heads, and
Dueling terminal/nonterminal replay updates. The local net10.0 build
passed with nullable flow analysis, analyzers and XML generation
disabled for memory limits. Full CI is queued serially after #2400.

Parameter vectors for InteractingLayer no longer contain gradient
buffers; vectors saved with those erroneous entries have a different
length.

Categorical embeddings use the tape-aware gather operation rather than a
batch-by-cardinality one-hot matrix. Published interaction weights
remain available after prediction, with an explicit KeepsActivation
rationale and regression coverage. Parameter updates invalidate
persistent GPU tensors.



The training path now delegates to
TapeTrainingStepper.EagerSgdObjectiveStep, preserving the central
tape/arena lifecycle and satisfying NoNewHandWrittenTapeTrainingLoop.
The local build and all 18 focused cases pass, including all three
training-loop ratchet checks and the six whole-model finite-difference
configurations. This fixes the single failure from the
opencl-foundations run on the previous head (185 other cases passed
there).

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: t <t@e.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ooples <3075685+ooples@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant