RiverRider commited on
Commit
aa2d4f1
·
verified ·
1 Parent(s): b84c017

Initial release: SRT-Adapter v8a (peer-review distribution)

Browse files
.gitattributes CHANGED
@@ -1,35 +1,5 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
  *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.safetensors filter=lfs diff=lfs merge=lfs -text
2
+ *.pt filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.pdf filter=lfs diff=lfs merge=lfs -text
5
+ *.jsonl filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
LICENSE ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of tracking or improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for describing the origin of the Work and
141
+ reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Support. While redistributing the Work or
166
+ Derivative Works thereof, You may choose to offer, and charge a
167
+ fee for, acceptance of support, warranty, indemnity, or other
168
+ liability obligations and/or rights consistent with this License.
169
+ However, in accepting such obligations, You may act only on Your
170
+ own behalf and on Your sole responsibility, not on behalf of any
171
+ other Contributor, and only if You agree to indemnify, defend,
172
+ and hold each Contributor harmless for any liability incurred by,
173
+ or claims asserted against, such Contributor by reason of your
174
+ accepting any such warranty or support.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ Copyright 2026 James Burton Lancaster
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
README.md ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ base_model: Qwen/Qwen2.5-7B
4
+ tags:
5
+ - adapter
6
+ - semiotics
7
+ - bifurcation-detection
8
+ - metapragmatics
9
+ - frozen-backbone
10
+ - pytorch
11
+ - custom_code
12
+ language:
13
+ - en
14
+ library_name: pytorch
15
+ pipeline_tag: feature-extraction
16
+ inference: false
17
+ ---
18
+
19
+ # SRT-Adapter v8a: Peer-Review Release
20
+
21
+ A peer-review distribution of the **Semiotic-Reflexive Transformer Adapter (SRT-Adapter)**, v8a generation, trained on top of a frozen `Qwen/Qwen2.5-7B`. Includes the trained weights, an inference-only loader, evaluation data, benchmark artifacts, and the paper.
22
+
23
+ > **Custom-code model.** This is not an `AutoModel`-loadable checkpoint. `AutoModel.from_pretrained(...)` will not work. Clone or download this repository and load the weights through the bundled `SRTAdapter` class. See [How to get started with the model](#how-to-get-started-with-the-model).
24
+
25
+ > **Training and research source code is held back during patent and publication review.** This package ships the architecture as **inference-only Python**, sufficient to load the weights and read out all four semiotic channels. Training pipelines, loss code, dataset construction, and the wider SRT framework are not included.
26
+
27
+ ---
28
+
29
+ ## Model details
30
+
31
+ | | |
32
+ |---|---|
33
+ | **Developed by** | James Burton Lancaster |
34
+ | **Model type** | Adapter (parameter-efficient side network) on a frozen causal language model |
35
+ | **Backbone** | `Qwen/Qwen2.5-7B` (7.6B params, frozen, bf16) |
36
+ | **Trainable parameters** | ~14.5M (0.19% of backbone) |
37
+ | **Language** | English |
38
+ | **License** | Apache-2.0 (adapter weights, code, and config) |
39
+
40
+ The SRT-Adapter bolts **semiotic awareness** onto a frozen 7B language model. It does not modify a single backbone parameter and does not degrade language modeling quality. It exposes four new readouts at every token position:
41
+
42
+ - a continuous 64-D **community vector** (which discourse community is speaking)
43
+ - per-layer **divergence vectors** (where meaning forks across communities)
44
+ - a continuous **reflexivity estimate** $\hat{r}$ (how contested is this token)
45
+ - a binary **regime classification** (subcritical / supercritical)
46
+
47
+ The v8a generation is the headline result of the paper: removing the discrete prototype basis used in v3–v7 leaves cross-entropy unchanged while substantially improving every encoder-geometry metric.
48
+
49
+ ---
50
+
51
+ ## Evaluation
52
+
53
+ All measured on `Qwen/Qwen2.5-7B`, with no backbone parameters touched.
54
+
55
+ | Metric | Unadapted Qwen | SRT-Adapter v8a |
56
+ |---|---|---|
57
+ | Validation cross-entropy (nats) | 2.71 | **2.63** |
58
+ | Reddit community recall@1 (35-class) | 0.029 (chance) | **0.484** (16.7× chance) |
59
+ | Archetype recall@1 (33-class, OOD) | 0.030 (chance) | **0.230** (7.6× chance) |
60
+ | Within/between cosine ratio | n/a | **2.016** (vs 1.006 prior) |
61
+ | Trajectory anisotropy expansion | n/a | **~325×** vs prototype baseline |
62
+ | Regime AUROC | n/a | **0.99** (ECE ≈ 0.001 on 351K tokens) |
63
+ | TruthfulQA hallucination AUROC (zero-shot) | n/a | **0.573** (no TruthfulQA in training) |
64
+ | Counterfactual community decoding | n/a | 0.00 disagreement (factual) / **0.95** (contested) |
65
+
66
+ Full reporting and version history (v3 → v8b): paper §5 and Appendix A.
67
+
68
+ ---
69
+
70
+ ## Lineage and validation history
71
+
72
+ This adapter is the production-scaling stage of a multi-year research program on computational semiotics. The architectural commitments and training objectives were validated in two prior stages on different backbones and datasets before this release. Treat the v8a numbers below as the latest checkpoint in a longer arc, not as a fresh proposal.
73
+
74
+ - **Stage 1 (synthetic validation, 2026-03).** Four core architectural claims (subspace specialization, community differentiation, divergence tracking, bifurcation detection) were tested on synthetic data with planted divergence signals. All four passed: linear-probe margin $\geq 0.15$ on each Peircean subspace, $3.28\times$ contested-vs-neutral cosine ratio, Spearman $\rho = 0.822$ on divergence tracking, 100% regime classification with $\Delta \hat{r} = 0.659$. Full record: [`VALIDATION_HISTORY.md`](VALIDATION_HISTORY.md), Stage 1.
75
+ - **Stage 2 (natural-language validation, 2026-03).** The full five-test suite was re-run on the Supabase semiotic news corpus (19K articles, 5 political communities, 141K Peircean sign annotations). All five tests passed at required thresholds: silhouette $1.45\times$, $2.29\times$ contested-vs-neutral divergence norm ratio, Pearson $r = 0.884$ correlation between $\hat{r}$ and external polarization, $1.31\times$ cross-topic transfer ratio, and 85% regime classification accuracy on held-out curated passages. Full record: [`VALIDATION_HISTORY.md`](VALIDATION_HISTORY.md), Stage 2.
76
+ - **Stage 3 Phase 1 (frozen-backbone integration on TinyLlama-1.1B, 2026-03 to 2026-04).** 105 training rounds (R21 through R105) on a frozen TinyLlama-1.1B backbone established that the semiotic modules transfer to production backbones. Community detection silhouette improved to $6.93\times$; $\hat{r}$ correlation with external polarization remained robust at 0.66; curated-passage regime classification reached 85%. Two tests plateaued on the sparse 2-community Supabase data (MAH divergence ratio at $1.05$ to $1.10\times$ vs. required $2.0\times$; cross-topic transfer at $1.03$ to $1.04\times$ vs. required $1.3\times$). The plateau triggered a data-first pivot to a denser corpus and a backbone capable of supporting it.
77
+ - **Stage 3 Scalable Implementation (this release, 2026-04).** v5 through v8a port the validated architecture onto Qwen 2.5-7B and the Reddit Discourse Corpus (35 communities, 1M training samples). v8a is the current best checkpoint. The headline gain is not the discovery of bifurcation detection (already established in Stages 1 and 2) but the demonstration that the framework scales to a 7B frozen backbone at 0.19% parameter overhead, with $\sim 325\times$ trajectory-anisotropy expansion and Reddit recall@1 at $16.7\times$ chance.
78
+
79
+ For the program-level theoretical foundation see Lancaster (2025), "The Treachery of Signs," SSRN [5987495](https://papers.ssrn.com/abstract=5987495). For the full prior architecture specification and Stage 1 + Stage 2 results see Lancaster (2026a), SSRN [6349978](https://papers.ssrn.com/abstract=6349978). The present paper reports Stage 3 Phase 1 plus the v5 through v8a Stage 3 Scalable progression.
80
+
81
+ ---
82
+
83
+ ## Versions and roadmap
84
+
85
+ - **v8a (this release).** Headline result. Removing the discrete prototype basis used in v3 through v7 leaves cross-entropy unchanged while substantially improving every encoder-geometry metric. All paper §5 numbers are measured on this checkpoint.
86
+ - **v8b.** A falsification run included in the paper. Pushing the supervised-contrastive objective harder partially undoes v8a's gains. Documented as a negative result.
87
+ - **v9 (in training).** An experimental generation that adds a target-norm penalty on the inject-back arm to attack the central open problem from §6.3 (the inject-back arm carries no measurable signal). Will be released as a follow-up revision on this repo *only if* it improves on v8a across multiple metrics. Otherwise it will be documented as an additional ablation in a future paper revision and v8a will remain canonical.
88
+
89
+ If you are reviewing the paper, use v8a. The model card will be updated with a `revision` tag if v9 ships as an upgrade.
90
+
91
+ ---
92
+
93
+ ## Package contents
94
+
95
+ ```
96
+ srt-adapter-v8a/
97
+ ├── README.md ← you are here
98
+ ├── LICENSE ← Apache-2.0
99
+ ├── paper.pdf ← preprint with full architecture spec (§3 + Appendix A)
100
+ ├── VALIDATION_HISTORY.md ← Stage 1 + Stage 2 + Stage 3 Phase 1 evidence summary
101
+ ├── config.json ← v8a hyperparameters and module dimensions
102
+ ├── adapter.safetensors ← v8a weights (~28 MB, safetensors, preferred)
103
+ ├── adapter.pt ← v8a weights (~28 MB, PyTorch state-dict, legacy)
104
+ ├── requirements.txt ← torch + transformers + numpy + safetensors
105
+ ├── src/
106
+ │ └── srt/ ← inference-only model code
107
+ │ ├── config.py ← config dataclasses
108
+ │ ├── adapter.py ← SRTAdapter (frozen-backbone wrapper)
109
+ │ └── modules/ ← CDH, MAH, RRM, BEN
110
+ ├── examples/
111
+ │ ├── README.md
112
+ │ └── load_and_score.py ← end-to-end demo, prints all 4 readouts
113
+ ├── data/
114
+ │ ├── DATA.md ← schema + reproduction instructions for the full corpus
115
+ │ ├── NOTICE ← copyright notice for bundled Reddit comments
116
+ │ ├── val_200.jsonl ← 200 held-out samples with per-token r_true labels
117
+ │ └── archetypes.json ← 33-class out-of-distribution archetype probe
118
+ └── benchmarks/
119
+ ├── curated_metrics.json ← reference metrics from paper §5
120
+ └── curated_traces.json ← per-token trace dumps used in plots
121
+ ```
122
+
123
+ > **Note on `benchmarks/curated_metrics.json`.** This file reports v8a numbers on a 100-passage curated probe (regime accuracy, per-layer divergence norms, community-protocol activations). The near-zero $\hat{r}$ vs $r_{\text{true}}$ Pearson on this slice is expected and is discussed in paper §5.7 ($\hat{r}$ tracks information density as much as contestedness on short curated passages). The headline Pearson and recall numbers in the Evaluation table above come from the full Reddit validation split, not this curated probe.
124
+
125
+ ### What's NOT in this package
126
+
127
+ - **Training pipelines, loss functions, and the dataset construction code.** Held back during patent and publication review.
128
+ - **The wider SRT research framework** (annotation pipeline, ablation harness, sweep tooling, instrumentation scripts).
129
+ - **The full 1M-sample training corpus.** Reddit's redistribution terms preclude bundling it; see [`data/DATA.md`](data/DATA.md) for schema and reproduction.
130
+ - **The Qwen 2.5-7B backbone weights.** Pulled from HuggingFace under the [Tongyi Qianwen License](https://huggingface.co/Qwen/Qwen2.5-7B/blob/main/LICENSE).
131
+
132
+ ---
133
+
134
+ ## How to get started with the model
135
+
136
+ ```bash
137
+ # 1. set up a venv (Python ≥ 3.10) and install deps
138
+ pip install -r requirements.txt
139
+
140
+ # 2. score a passage end-to-end
141
+ cd examples
142
+ python load_and_score.py --text "Vaccine mandates are an obvious public health win."
143
+ ```
144
+
145
+ First run downloads `Qwen/Qwen2.5-7B` (~15 GB) from HuggingFace. The example loads `adapter.safetensors` by default and falls back to `adapter.pt` if the safetensors file is absent.
146
+
147
+ For a programmatic-use snippet, see [`examples/README.md`](examples/README.md).
148
+
149
+ ---
150
+
151
+ ## Uses
152
+
153
+ The adapter is most useful as a **diagnostic instrument** for what a frozen language model already encodes about discourse structure. Some concrete patterns:
154
+
155
+ ### 1. Per-token contestedness scoring
156
+
157
+ Use BEN's regime logits to flag which token positions in a passage sit in a contested-meaning regime. Useful for:
158
+
159
+ - highlighting ideologically loaded spans in user-generated text
160
+ - routing inputs to human review when supercritical regime probability exceeds a threshold
161
+ - annotating debate transcripts, news comment threads, or policy documents with per-token tension scores
162
+
163
+ ### 2. Unsupervised discourse-community clustering
164
+
165
+ The 64-D community vector from CDH supports nearest-neighbor retrieval and clustering without ever needing community labels at inference. Useful for:
166
+
167
+ - segmenting a corpus by latent discourse community (recall@1 = 16.7× chance on 35 known communities)
168
+ - retrieving thematically aligned passages for downstream modeling
169
+ - detecting coordinated-inauthentic-behavior signatures via tight community clustering of supposedly independent accounts
170
+
171
+ ### 3. Counterfactual community-conditioned decoding
172
+
173
+ By steering the community vector at decode time, you can ask "how would *this* community complete this sentence?" Useful for:
174
+
175
+ - cross-community simulation studies (the paper measures 0.95 mean disagreement on contested prompts vs 0.00 on factual ones)
176
+ - synthetic-disagreement generation for training argument-mining or stance-detection systems
177
+ - audit / red-team probes that surface latent assumptions across reader communities
178
+
179
+ ### 4. Hallucination signal for retrieval and generation
180
+
181
+ $\hat{r}$ correlates with epistemic instability and gives a usable zero-shot AUROC of 0.573 on TruthfulQA. Useful as:
182
+
183
+ - a feature in hallucination classifiers (alongside other signals)
184
+ - a per-token routing signal for retrieval-augmented generation: high $\hat{r}$ tokens warrant a retrieval round
185
+ - a calibration probe in evaluation pipelines
186
+
187
+ ### 5. Feature extraction for downstream classifiers
188
+
189
+ The MAH divergence vectors (3 × 256-D per token) are usable as semiotic features in any downstream classifier without retraining the backbone or the adapter. Useful for:
190
+
191
+ - stance and frame classification on small labeled sets
192
+ - author / community attribution
193
+ - topical drift detection across long documents
194
+
195
+ ### 6. Reviewer probes against the paper's claims
196
+
197
+ `data/val_200.jsonl` ships with per-token `r_true` labels. Reviewers can validate claims about $\hat{r}$ correlation, regime accuracy, and divergence norms against the bundled `benchmarks/curated_metrics.json` without rerunning training.
198
+
199
+ ### Out-of-scope uses
200
+
201
+ - **Treating $\hat{r}$ as a calibrated truth score.** It correlates with information density as much as contestedness; see paper §5.7.
202
+ - **Expecting the inject-back arm to noticeably change generation.** The observation half is well-formed; the intervention half does not yet carry signal. See paper §6.3 and §6.5.
203
+ - **Safety-critical decisions without independent validation.** The adapter is a research instrument, not a deployed safety system.
204
+ - **Generation models.** The adapter does not improve text generation quality; it adds structured side-channel readouts.
205
+
206
+ ---
207
+
208
+ ## Training details
209
+
210
+ ### Training data
211
+
212
+ - **Corpus:** ~1M Reddit comments × 35 discourse communities, with per-token reflexivity labels and chain-of-interpretants annotations.
213
+ - The full corpus is **not redistributed**; see [`data/DATA.md`](data/DATA.md) for schema and reproduction.
214
+ - A 200-sample held-out evaluation subset is bundled in [`data/val_200.jsonl`](data/val_200.jsonl) under the terms in [`data/NOTICE`](data/NOTICE).
215
+
216
+ ### Training procedure
217
+
218
+ - Optimizer: AdamW, learning rate 3e-4, batch 16, max sequence length 512.
219
+ - Schedule: 3 epochs over 1M samples, early-stopped at ~10K steps based on validation cross-entropy.
220
+ - Backbone: frozen, bf16. No backbone parameter is updated.
221
+ - Hardware: single NVIDIA A6000 (48 GB).
222
+
223
+ ---
224
+
225
+ ## Architecture summary
226
+
227
+ | Module | Reads | Outputs | Paper section |
228
+ |---|---|---|---|
229
+ | **Community Discovery Head** (CDH) | layer 4 hidden states, mean-pooled | continuous 64-D community vector (no prototype basis under v8a) | §3.2 |
230
+ | **Metapragmatic Attention Heads** (MAH) | layers 7, 14, 21 | per-token divergence vectors (256-D × 3 layers) | §3.3 |
231
+ | **Reflexive Recurrent Module** (RRM) | accumulated divergence | 512-D GRU meta-state; FiLM injections back into layers 14, 21 | §3.4 |
232
+ | **Bifurcation Estimation Network** (BEN) | meta-state | per-token $\hat{r}$ + 2-way regime logits | §3.5 |
233
+
234
+ Full module specifications and loss decomposition: paper §3, §4, and Appendix A.
235
+
236
+ ---
237
+
238
+ ## Bias, risks, and limitations
239
+
240
+ The paper publishes its failures in full. In short:
241
+
242
+ 1. **The inject-back arm currently carries no measurable signal.** Ablating it changes nothing on validation. Central open problem; v9 targets this.
243
+ 2. **$\hat{r}$ tracks information density as much as contestedness.** Useful signal, not a clean reading of "is this token contested."
244
+ 3. **The 33 archetypes collapse to ~4 functional clusters.** Read as a finding (the geometry resists fine-grained quantization), not a classification bug.
245
+ 4. **v8b is a falsification.** Pushing the supervised-contrastive objective harder partially undoes v8a's gains.
246
+ 5. **Backbone dependence.** Only validated on Qwen 2.5-7B. Module dimensions are tied to the backbone's hidden size (3584).
247
+ 6. **Training corpus bias.** Reddit comments skew English, US-centric, and over-represent argumentative and politically charged communities. The community vector geometry inherits those biases. Treat community recall and counterfactual-decoding numbers as descriptive, not normative.
248
+
249
+ ---
250
+
251
+ ## Citation
252
+
253
+ ```bibtex
254
+ @article{lancaster2026srtadapter,
255
+ title = {Semiotic Taps: Lightweight Adapter Modules for Bifurcation
256
+ Detection in Frozen Language Models},
257
+ author = {Lancaster, James Burton},
258
+ year = {2026},
259
+ note = {Preprint, peer-review distribution}
260
+ }
261
+
262
+ @article{lancaster2026srtpreprint,
263
+ title = {Semiotic-Reflexive Language Model Training: Bridging
264
+ Interpretive Bifurcations through Metapragmatic Chain
265
+ Architectures and Embodied Grounding},
266
+ author = {Lancaster, James Burton},
267
+ year = {2026},
268
+ journal = {SSRN},
269
+ url = {https://papers.ssrn.com/abstract=6349978}
270
+ }
271
+
272
+ @article{lancaster2025treachery,
273
+ title = {The Treachery of Signs: Semiotic Mediation, Pitchfork
274
+ Bifurcation, and Political Polarization in Algorithmically
275
+ Curated Societies},
276
+ author = {Lancaster, James Burton},
277
+ year = {2025},
278
+ journal = {SSRN},
279
+ url = {https://papers.ssrn.com/abstract=5987495}
280
+ }
281
+ ```
282
+
283
+ Full reference list (Peirce, Wildgen, Anderson, Silverstein, Kockelman, Evans, von Foerster, Maturana & Varela, Leighton, VanSaders, Bennett, Landauer, Parrondo, and others) in [`paper.pdf`](paper.pdf).
284
+
285
+ ---
286
+
287
+ ## License
288
+
289
+ - **Adapter weights, inference code, config, benchmark artifacts, archetype data, and this package:** Apache-2.0 ([`LICENSE`](LICENSE)).
290
+ - **Validation samples in `data/val_200.jsonl`:** included for research reproduction; comments remain the intellectual property of their original Reddit authors. See [`data/NOTICE`](data/NOTICE).
291
+ - **Training pipelines, dataset construction, and the wider SRT framework:** held back during patent and publication review. Not included.
292
+ - **Qwen 2.5-7B backbone:** governed separately by the [Tongyi Qianwen License](https://huggingface.co/Qwen/Qwen2.5-7B/blob/main/LICENSE).
293
+
294
+ ---
295
+
296
+ ## Contact
297
+
298
+ For training-code access, reproduction questions, or follow-up: see paper PDF for current author contact details.
SHA256SUMS ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 80337123382f5786be2f87326388ba6cee2ebb7be1afc2c012df7e68aa753e8b ./.gitattributes
2
+ 161a873e893d3af606c92866401b329204b127585008ec1894ff9a0f618d86f2 ./LICENSE
3
+ e1638fc045b16eea6074670198ee7d05c07b7bb53ecc59926c5758ec22ed28a4 ./README.md
4
+ 777c77fe0d4bdc0361fa907b313233eea513f20bb87b69135e128d5a2e9a3f5b ./SHA256SUMS
5
+ 025681e1f01fb754ae7f1ddbe87246dadd2b55d2ff62039ba73051428f12e5d5 ./VALIDATION_HISTORY.md
6
+ 5d201ed58c770d6c8f0fb894e4931fe5923efba455ac4994c7a97f01d13aa05c ./adapter.pt
7
+ 635d4dc76c3c21743ce92988aa107d9fe36ed0b2db53a7eb73b258608741204e ./adapter.safetensors
8
+ 855a5f1a9f5478811d6a8deeb276d4eae4e0211d28041d053bbea3a50afa3c7e ./benchmarks/curated_metrics.json
9
+ 500d9c7b9c9deb0a76482a80dd7d6a8c8b1e648a75f6d5a6732c384baa543d54 ./benchmarks/curated_traces.json
10
+ a83afa4f3524ce4c81f359e197c1ed30bca2753e09b7deea82145b36fc103cc6 ./config.json
11
+ c83c36c374202585c14dc437a2e8ad5b21dbbfbecc7ebd68a80fc3c1494e3d72 ./data/DATA.md
12
+ 7d12f72f5cf6e4a451d4dca06c82b5d37c0456e4d577cb63af7ef6c35efadda2 ./data/NOTICE
13
+ e4c8cc4b329bf0b6d9b2070dc386c04b730232fa8c6b310a818ec09d38b1b2e1 ./data/archetypes.json
14
+ e2772cba759bd8fa1a7a5c83f2913141bcd3c88ebdbcfc1069dad5d81366df23 ./data/val_200.jsonl
15
+ 6edfa5b72f1b3e799ed37eb881d620f92217423f3262b9c19ff4d356e8047b13 ./examples/README.md
16
+ 9a8ea6f13b1cf905a07c3f9707233a5cec7d4c13a6c2ccb3f3dd12c071371653 ./examples/load_and_score.py
17
+ 58dac62a9ac6b0cc4ae35bf3d8297f75a43b7ab1f1e55be8c71c9ff76474cf87 ./paper.pdf
18
+ 0ad957410e78eaaeb6b0369e52bf14aa5026bb02aaa4546a09b35d0e52a71c8d ./requirements.txt
19
+ 133e1537baa6e11a0df942755498da8b1d48a3aab736db0b8206fc2db6393454 ./src/srt/__init__.py
20
+ b77e7e1320ac02590234953ff0c8a893b645208b43fa1d2c4942da5eeba96a8b ./src/srt/adapter.py
21
+ 64007a7c8f9a77ea3e91d543a15fd13b7f05e3c781f405e30e2eaa018fd3ec4e ./src/srt/config.py
22
+ 8e2291d2b5e05282994fbac8ab4a9462054ff864a741e4d35a15bcc3961bae54 ./src/srt/modules/__init__.py
23
+ 94d88e7e10222dcb103d7d8baad41a68a23e596f458b24bfc9ed5215500891ce ./src/srt/modules/ben.py
24
+ e686f499b0776e0354df5d8de0698d081ee194c580ac7d162b58f20136be8d08 ./src/srt/modules/community.py
25
+ 50075c7850603d459f876f14135d24cf3bf491adf5553d4cf918416ac3c245c2 ./src/srt/modules/mah.py
26
+ c4e74600fd8a78f77f691db13b71ab751fd52d2fae4ecb7bdf4c1e6e95cb8f39 ./src/srt/modules/rrm.py
VALIDATION_HISTORY.md ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Validation history of the SRT program
2
+
3
+ This document distills the validation evidence behind the SRT adapter (v8a)
4
+ into a single self-contained record. It covers the three validation stages
5
+ that established the architectural claims this adapter inherits:
6
+
7
+ - **Stage 1**. Synthetic controlled experiments (4/4 tests passed).
8
+ - **Stage 2**. Natural-language validation on a real news corpus (5/5
9
+ tests passed).
10
+ - **Stage 3 Phase 1**. Hybrid model on a frozen pretrained backbone
11
+ (4/5 tests passed at the first gate evaluation; the regime-classification
12
+ test motivated the remediation campaign that produced the lightweight
13
+ adapter program reported in `paper.pdf`).
14
+
15
+ For the canonical theoretical record, see Lancaster (2025), "The Treachery
16
+ of Signs," SSRN [5987495](https://papers.ssrn.com/abstract=5987495), and
17
+ Lancaster (2026a), "Semiotic-Reflexive Language Model Training," SSRN
18
+ [6349978](https://papers.ssrn.com/abstract=6349978). The numbers below
19
+ appear in those papers in their full original context; this file is the
20
+ concise summary referenced from the model card.
21
+
22
+ ---
23
+
24
+ ## Stage 1: Synthetic controlled experiments
25
+
26
+ **Gate G1: PASSED (4/4).** Each test isolates one architectural claim on
27
+ synthetic data with planted ground-truth signals.
28
+
29
+ ### 1.1 Subspace specialization (linear probing)
30
+
31
+ | Task | Target subspace | Target acc | Control acc | Margin | Threshold |
32
+ |---|---|---|---|---|---|
33
+ | Token identity | Representamen | 99.31% | 1.31% | 0.980 | $\geq 0.15$ |
34
+ | Community membership | Interpretant | 100.00% | 64.42% | 0.356 | $\geq 0.15$ |
35
+ | Attractor basin | Attractor | 100.00% | 84.52% | 0.155 | $\geq 0.15$ |
36
+ | Position in sequence | Object | 43.22% | 12.99% | 0.302 | $\geq 0.15$ |
37
+
38
+ The four Peircean subspaces encode qualitatively different information.
39
+
40
+ ### 1.2 Community differentiation
41
+
42
+ | Metric | Value |
43
+ |---|---|
44
+ | Mean cosine distance, contested signs (20 words) | 0.3622 |
45
+ | Mean cosine distance, neutral signs (79 words) | 0.1103 |
46
+ | Ratio | $3.28\times$ (threshold $\geq 3.0\times$) |
47
+
48
+ ### 1.3 Divergence tracking
49
+
50
+ | Metric | Value |
51
+ |---|---|
52
+ | Spearman $\rho$ between $\hat{r}$ and $r_{\text{true}}$ | 0.8220 ($p \approx 0$) |
53
+ | Samples | 64,000 |
54
+ | Threshold | $\rho \geq 0.6$ |
55
+
56
+ ### 1.4 Bifurcation detection
57
+
58
+ | Metric | Value |
59
+ |---|---|
60
+ | Mean $\hat{r}$ difference, post minus pre | 0.6588 (threshold $> 0.2$) |
61
+ | Regime classification accuracy | 100.00% (threshold $> 75$%) |
62
+ | Samples | 500 |
63
+
64
+ ---
65
+
66
+ ## Stage 2: Natural-language validation
67
+
68
+ **Gate G2: PASSED (5/5).** The full architecture re-tested on a curated
69
+ news corpus (5 communities, 19K articles, 141K Peircean sign annotations,
70
+ contested terms including *freedom*, *justice*, *patriot*).
71
+
72
+ ### 2.1 Community embedding structure
73
+
74
+ | Metric | Value |
75
+ |---|---|
76
+ | Contested silhouette | 0.5293 (threshold $> 0.15$) |
77
+ | Neutral silhouette | 0.3653 |
78
+ | Silhouette ratio (contested over neutral) | $1.45\times$ (threshold $> 1.3\times$) |
79
+ | Samples | 5,000 contested + 5,000 neutral |
80
+ | Communities | 5 |
81
+
82
+ ### 2.2 Divergence vectors on contested terms
83
+
84
+ | Metric | Value |
85
+ |---|---|
86
+ | Group A (divergent connections) mean | 15.3730 |
87
+ | Group B (referential only) mean | 6.7001 |
88
+ | Ratio | $2.29\times$ (threshold $\geq 2.0\times$) |
89
+ | Cohen's $d$ | 0.378 |
90
+ | Tokens | 81,839 vs 5,047 |
91
+
92
+ ### 2.3 $\hat{r}$ vs external polarization
93
+
94
+ | Metric | Value |
95
+ |---|---|
96
+ | Pearson $r$ | 0.8843 ($p \approx 0$) |
97
+ | Threshold | $r \geq 0.3$ |
98
+ | Samples | 2,120 |
99
+ | Mean $\hat{r}$ | 0.3257 |
100
+ | Mean external divergence | 0.3759 |
101
+
102
+ ### 2.4 Cross-topic transfer (zero-shot)
103
+
104
+ | Metric | Value |
105
+ |---|---|
106
+ | Held-out contested mean divergence | 19.1582 (45,601 tokens) |
107
+ | Held-out neutral mean divergence | 14.6394 (1,265 tokens) |
108
+ | Ratio | $1.31\times$ (threshold $> 1.3\times$) |
109
+
110
+ ### 2.5 Regime classification on curated passages
111
+
112
+ | Metric | Value |
113
+ |---|---|
114
+ | Accuracy | 85.00% (threshold $\geq 70$%) |
115
+ | ROC AUC | 0.8988 |
116
+ | Mean $\hat{r}$ low-divergence (50 passages) | 0.2845 ± 0.0619 |
117
+ | Mean $\hat{r}$ high-divergence (50 passages) | 0.4439 ± 0.0931 |
118
+ | Cohen's $d$ | 2.016 |
119
+
120
+ ---
121
+
122
+ ## Stage 3 Phase 1: Hybrid model on a frozen backbone
123
+
124
+ **Gate G3a: 3/5 at end of campaign (R21 through R105).** The full Stage-2
125
+ architecture was grafted onto a frozen TinyLlama-1.1B backbone. The first
126
+ gate evaluation (R21) inverted Stage 2's failure pattern: four geometric
127
+ tests passed but the regime-classification head collapsed to a
128
+ supercritical bias (47% accuracy, well below the 70% threshold). A
129
+ 105-round remediation campaign followed (R21 through R105), spanning
130
+ gradient isolation of the bifurcation-estimation network, BEN-input
131
+ detachment, dual-checkpoint tracking, calign and dmag re-weighting, and
132
+ fresh-start retraining with all fixes applied from step 0.
133
+
134
+ By R105 the regime-classification failure was resolved (85.00%) and the
135
+ community-embedding silhouette ratio improved by roughly 3x over R21.
136
+ However, two tests that had passed at R21 plateaued during the
137
+ remediation campaign and never recovered to threshold on the
138
+ 2-community Supabase corpus.
139
+
140
+ ### Initial gate (R21) versus end of campaign (R105)
141
+
142
+ | Test | Stage 2 | R21 baseline | R105 final | Threshold | R105 status |
143
+ |---|---|---|---|---|---|
144
+ | Community embedding (silhouette ratio) | $1.45\times$ | $2.18\times$ | $\sim 6.93\times$ | $> 1.3\times$ | **PASS** |
145
+ | Divergence ratio (contested over neutral) | $2.29\times$ | $5.91\times$ | $\sim 1.05$ to $1.10\times$ | $\geq 2.0\times$ | **FAIL** (plateau) |
146
+ | $\hat{r}$ vs external polarization (Pearson) | 0.88 | 0.65 | $\sim 0.66$ | $\geq 0.3$ | **PASS** |
147
+ | Cross-topic transfer (held-out ratio) | $1.31\times$ | $6.10\times$ | $\sim 1.03$ to $1.04\times$ | $> 1.3\times$ | **FAIL** (plateau) |
148
+ | Regime classification accuracy | 85.00% | 47.00% | 85.00% | $\geq 70$% | **PASS** |
149
+
150
+ ### Diagnosis and pivot
151
+
152
+ The diagnosis was that the 2-community Supabase corpus was too sparse to
153
+ support discriminative divergence-vector training at the scale needed
154
+ for a frozen-backbone integration. The two plateaued tests measure
155
+ contested-over-neutral norm ratios on the MAH divergence vectors; once
156
+ the supervised-contrastive objective reached its data ceiling, no
157
+ further architectural change moved them.
158
+
159
+ That diagnosis triggered the data-first pivot to a denser corpus (the
160
+ 35-community Reddit Discourse Corpus, ~1M training samples) and a
161
+ larger frozen backbone (Qwen 2.5-7B). The Stage 3 Scalable line that
162
+ followed (v3 through v8a) is the production form of that pivot. The
163
+ adapter released in this package (v8a) inherits the validated geometry
164
+ (community embedding, polarization estimation, regime classification)
165
+ and re-establishes the divergence-norm contrast on the denser corpus
166
+ through a gradient-isolated adapter rather than a from-scratch hybrid
167
+ model. The inject-back arm of v8a remains under-developed and is the
168
+ central open problem identified in §5.1 and §6.3 of `paper.pdf`.
169
+
170
+ ---
171
+
172
+ ## Cross-stage capability summary
173
+
174
+ | Capability | Stage 1 | Stage 2 | Stage 3 Phase 1 (R105) | Comment |
175
+ |---|---|---|---|---|
176
+ | Subspace specialization | $\checkmark$ | --- | --- | Stage-1-only test |
177
+ | Community embedding | $\checkmark$ ($3.28\times$) | $\checkmark$ ($1.45\times$) | $\checkmark$ ($\sim 6.93\times$) | Improves with backbone + remediation |
178
+ | Divergence tracking | $\checkmark$ ($\rho = 0.82$) | $\checkmark$ ($2.29\times$) | plateau ($\sim 1.05$ to $1.10\times$) | Data-ceiling on Supabase corpus |
179
+ | Polarization estimation | $\checkmark$ ($\rho = 0.82$) | $\checkmark$ ($r = 0.88$) | $\checkmark$ ($r \approx 0.66$) | Modest regression |
180
+ | Bifurcation detection | $\checkmark$ (100%) | $\checkmark$ (85%) | $\checkmark$ (85%, R105) | Recovered through remediation |
181
+ | Cross-topic transfer | --- | $\checkmark$ ($1.31\times$) | plateau ($\sim 1.03$ to $1.04\times$) | Data-ceiling on Supabase corpus |
182
+
183
+ ---
184
+
185
+ ## Provenance
186
+
187
+ The Stage 1 and Stage 2 numbers were produced by the standalone SRT
188
+ architecture (~21M trainable parameters) on synthetic and curated
189
+ news-corpus data. The Stage 3 Phase 1 numbers were produced by the
190
+ hybrid configuration (frozen TinyLlama-1.1B backbone plus the same
191
+ SRT modules) across 105 training rounds (R21 through R105). The v8a
192
+ adapter released in this package takes the program forward to a frozen
193
+ Qwen 2.5-7B backbone and the 35-community Reddit Discourse Corpus with
194
+ a re-engineered, gradient-isolated adapter (~14.5M trainable),
195
+ preserving the validated capabilities and improving validation
196
+ cross-entropy from 2.71 (no-adapter baseline) to 2.63 (v8a). See the
197
+ v3 through v8a results in §5 of `paper.pdf` and the lineage discussion
198
+ in §1.1.5 and §2.0 of `paper.pdf`.
adapter.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5d201ed58c770d6c8f0fb894e4931fe5923efba455ac4994c7a97f01d13aa05c
3
+ size 29134545
adapter.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:635d4dc76c3c21743ce92988aa107d9fe36ed0b2db53a7eb73b258608741204e
3
+ size 29124982
benchmarks/curated_metrics.json ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "n_samples": 100,
3
+ "n_tokens_masked": 16434,
4
+ "mean_ce": 2.565993104914647,
5
+ "wall_time_sec": 9.431363582611084,
6
+ "pearson_r_hat_vs_r_true_raw": 0.004732293542474508,
7
+ "pearson_r_hat_vs_r_true_compressed": 0.004732287954539061,
8
+ "r_hat_mean": 0.62109375,
9
+ "r_hat_std": 0.361328125,
10
+ "r_hat_min": -0.2373046875,
11
+ "r_hat_max": 1.0,
12
+ "r_hat_saturated_pos": 0.3172082304954529,
13
+ "r_hat_saturated_neg": 0.0,
14
+ "regime_accuracy": 0.7566630244255066,
15
+ "regime_accuracy_subcritical": 0.0802631601691246,
16
+ "regime_accuracy_supercritical": 0.9101836681365967,
17
+ "regime_true_pos_frac": 0.8150176405906677,
18
+ "divergence_norms_per_layer": {
19
+ "0": {
20
+ "mean": 1.1829235553741455,
21
+ "std": 0.982042133808136,
22
+ "min": 0.392578125,
23
+ "max": 18.75,
24
+ "median": 0.9609375
25
+ },
26
+ "1": {
27
+ "mean": 1.0742521286010742,
28
+ "std": 0.745279848575592,
29
+ "min": 0.474609375,
30
+ "max": 15.625,
31
+ "median": 0.953125
32
+ },
33
+ "2": {
34
+ "mean": 1.0943559408187866,
35
+ "std": 0.7176513671875,
36
+ "min": 0.4375,
37
+ "max": 8.0,
38
+ "median": 0.84765625
39
+ }
40
+ },
41
+ "injection_norms_per_layer": {
42
+ "0": {
43
+ "mean": 1.010841727256775,
44
+ "std": 0.06084292009472847,
45
+ "min": 0.86328125,
46
+ "max": 3.28125,
47
+ "median": 1.0
48
+ },
49
+ "1": {
50
+ "mean": 1.008637547492981,
51
+ "std": 0.02614293247461319,
52
+ "min": 0.859375,
53
+ "max": 1.296875,
54
+ "median": 1.0078125
55
+ }
56
+ },
57
+ "community_protocol_activation": {
58
+ "community_ids": [
59
+ 1,
60
+ 2,
61
+ 3,
62
+ 4,
63
+ 5
64
+ ],
65
+ "matrix": [
66
+ [
67
+ 0.03024902381002903,
68
+ 0.03867187350988388,
69
+ 0.02988281287252903,
70
+ 0.037841796875,
71
+ 0.03066406212747097,
72
+ 0.02822265587747097,
73
+ 0.02749023400247097,
74
+ 0.03093261644244194,
75
+ 0.03215331956744194,
76
+ 0.03151855617761612,
77
+ 0.02800292894244194,
78
+ 0.03525390475988388,
79
+ 0.031982421875,
80
+ 0.03007812425494194,
81
+ 0.03364257887005806,
82
+ 0.0279541015625,
83
+ 0.03095703199505806,
84
+ 0.0316162109375,
85
+ 0.02922363206744194,
86
+ 0.02790527418255806,
87
+ 0.02670898474752903,
88
+ 0.0238037109375,
89
+ 0.0361328125,
90
+ 0.03439941257238388,
91
+ 0.03312988206744194,
92
+ 0.03432617336511612,
93
+ 0.02426757849752903,
94
+ 0.03449707105755806,
95
+ 0.0341796875,
96
+ 0.03354492038488388,
97
+ 0.02866210974752903,
98
+ 0.03217773512005806
99
+ ],
100
+ [
101
+ 0.03056640550494194,
102
+ 0.03090820275247097,
103
+ 0.02940673753619194,
104
+ 0.03652343899011612,
105
+ 0.03116455115377903,
106
+ 0.03427734225988388,
107
+ 0.03038330003619194,
108
+ 0.03007812425494194,
109
+ 0.03364257887005806,
110
+ 0.03167724609375,
111
+ 0.03090820275247097,
112
+ 0.03162841871380806,
113
+ 0.03594970703125,
114
+ 0.03012695349752903,
115
+ 0.03232421725988388,
116
+ 0.02933349646627903,
117
+ 0.03422851487994194,
118
+ 0.03110351599752903,
119
+ 0.02978515625,
120
+ 0.02908935584127903,
121
+ 0.02882080152630806,
122
+ 0.02468261681497097,
123
+ 0.03054199181497097,
124
+ 0.03554687649011612,
125
+ 0.02912597730755806,
126
+ 0.03128661960363388,
127
+ 0.02904052659869194,
128
+ 0.03438720852136612,
129
+ 0.03316650539636612,
130
+ 0.03322754055261612,
131
+ 0.02694091759622097,
132
+ 0.03029785118997097
133
+ ],
134
+ [
135
+ 0.03065400943160057,
136
+ 0.034294575452804565,
137
+ 0.03066837042570114,
138
+ 0.03257841244339943,
139
+ 0.03239171579480171,
140
+ 0.03334314748644829,
141
+ 0.03047090396285057,
142
+ 0.0294189453125,
143
+ 0.03157312795519829,
144
+ 0.03188907355070114,
145
+ 0.030880197882652283,
146
+ 0.03213680535554886,
147
+ 0.03692267835140228,
148
+ 0.03207577019929886,
149
+ 0.030205221846699715,
150
+ 0.030065199360251427,
151
+ 0.03467155992984772,
152
+ 0.031731098890304565,
153
+ 0.02859317511320114,
154
+ 0.028384938836097717,
155
+ 0.027544807642698288,
156
+ 0.024575626477599144,
157
+ 0.03175623342394829,
158
+ 0.03575583174824715,
159
+ 0.028223374858498573,
160
+ 0.033975038677453995,
161
+ 0.02731502801179886,
162
+ 0.034980326890945435,
163
+ 0.03118896484375,
164
+ 0.03532140329480171,
165
+ 0.026683134958148003,
166
+ 0.029670266434550285
167
+ ],
168
+ [
169
+ 0.030530428513884544,
170
+ 0.033068206161260605,
171
+ 0.03035053424537182,
172
+ 0.02593030408024788,
173
+ 0.03373637795448303,
174
+ 0.03590794652700424,
175
+ 0.032971832901239395,
176
+ 0.0279541015625,
177
+ 0.031307823956012726,
178
+ 0.03224583715200424,
179
+ 0.032335784286260605,
180
+ 0.031211450695991516,
181
+ 0.039467260241508484,
182
+ 0.03353721275925636,
183
+ 0.027523642405867577,
184
+ 0.03140419349074364,
185
+ 0.03579872474074364,
186
+ 0.03205952048301697,
187
+ 0.027960525825619698,
188
+ 0.0286865234375,
189
+ 0.02707391045987606,
190
+ 0.026129471138119698,
191
+ 0.032387182116508484,
192
+ 0.037732575088739395,
193
+ 0.0260009765625,
194
+ 0.035098426043987274,
195
+ 0.02888569049537182,
196
+ 0.03593364357948303,
197
+ 0.02692614123225212,
198
+ 0.03743061423301697,
199
+ 0.025127209722995758,
200
+ 0.027279501780867577
201
+ ],
202
+ [
203
+ 0.029689788818359375,
204
+ 0.03560638427734375,
205
+ 0.0307769775390625,
206
+ 0.0432281494140625,
207
+ 0.030292510986328125,
208
+ 0.02777099609375,
209
+ 0.028041839599609375,
210
+ 0.0316619873046875,
211
+ 0.029689788818359375,
212
+ 0.031291961669921875,
213
+ 0.027507781982421875,
214
+ 0.032047271728515625,
215
+ 0.030048370361328125,
216
+ 0.031558990478515625,
217
+ 0.0352783203125,
218
+ 0.02927398681640625,
219
+ 0.0336761474609375,
220
+ 0.030902862548828125,
221
+ 0.02973175048828125,
222
+ 0.0284881591796875,
223
+ 0.026569366455078125,
224
+ 0.02393341064453125,
225
+ 0.034854888916015625,
226
+ 0.03473663330078125,
227
+ 0.031063079833984375,
228
+ 0.033145904541015625,
229
+ 0.024745941162109375,
230
+ 0.034252166748046875,
231
+ 0.035671234130859375,
232
+ 0.03212738037109375,
233
+ 0.02909088134765625,
234
+ 0.0332489013671875
235
+ ]
236
+ ],
237
+ "K": 32
238
+ },
239
+ "community_top_prototype_mass_mean": 0.038962680101394656,
240
+ "community_pairwise_cos_sim_mean": 0.9949159622192383
241
+ }
benchmarks/curated_traces.json ADDED
The diff for this file is too large to render. See raw diff
 
config.json ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backbone_id": "Qwen/Qwen2.5-7B",
3
+ "backbone_dtype": "bfloat16",
4
+ "mah_layer_indices": [
5
+ 7,
6
+ 14,
7
+ 21
8
+ ],
9
+ "rrm_inject_indices": [
10
+ 14,
11
+ 21
12
+ ],
13
+ "community_layer_idx": 4,
14
+ "num_mah_layers": 3,
15
+ "mah": {
16
+ "d_sub": 512,
17
+ "d_divergence": 256,
18
+ "num_heads": 4,
19
+ "dropout": 0.1
20
+ },
21
+ "rrm": {
22
+ "d_meta": 512,
23
+ "inject_scale": 1.0
24
+ },
25
+ "ben": {
26
+ "d_hidden": 256
27
+ },
28
+ "community": {
29
+ "num_prototypes": 32,
30
+ "d_community": 64,
31
+ "temperature": 1.0,
32
+ "use_prototypes": false
33
+ },
34
+ "loss": {
35
+ "ce_weight": 1.0,
36
+ "chain_weight": 0.5,
37
+ "bif_weight": 1.0,
38
+ "regime_weight": 5.0,
39
+ "div_alive_weight": 0.1,
40
+ "inject_reg_weight": 0.0,
41
+ "inject_target_norm": 1.0,
42
+ "community_entropy_weight": 0.01,
43
+ "community_supcon_weight": 2.0,
44
+ "community_supcon_temperature": 0.1,
45
+ "divergence_supcon_weight": 0.3,
46
+ "divergence_supcon_temperature": 0.1,
47
+ "listnet_weight": 0.5,
48
+ "listnet_temperature": 1.0,
49
+ "chain_residual_aux_weight": 0.05,
50
+ "chain_residual_aux_target": 0.5
51
+ }
52
+ }
data/DATA.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Data
2
+
3
+ ## What's in this folder
4
+
5
+ - `val_200.jsonl`. 200 held-out validation samples from the SRT-Adapter Reddit corpus, with per-token reflexivity (`r_true`) and chain-of-interpretants labels. Sufficient for smoke-testing inference and reproducing the per-passage trace artifacts.
6
+ - `archetypes.json`. 33 hand-curated discourse archetypes used for the out-of-distribution probe (Section 5.8 of the paper). Each entry is a (label, prompt-set) pair.
7
+
8
+ ## Schema (`val_200.jsonl`)
9
+
10
+ One JSON object per line:
11
+
12
+ | field | type | description |
13
+ |---|---|---|
14
+ | `text` | string | raw passage |
15
+ | `community_id` | int | Reddit community index (1–35) |
16
+ | `community_label` | string | e.g. `reddit:AskTrumpSupporters` |
17
+ | `r_true` | list[float] | per-token reflexivity score in [0, 1] |
18
+ | `chain_labels` | list[int] | per-token chain-of-interpretants supervision |
19
+ | `source` | string | corpus source tag |
20
+ | `domain` | string | coarse topical domain |
21
+ | `metadata` | object | original Reddit metadata (subreddit, score, etc.) |
22
+
23
+ ## Full corpus (not redistributed here)
24
+
25
+ The full training corpus is **1,000,000** Reddit comments spanning the 35 listed communities; the held-out validation set is **100,000** samples drawn from the same schema. Neither is redistributed in this release because:
26
+
27
+ 1. Reddit's content terms restrict bulk redistribution.
28
+ 2. The corpus is reproducible from the public Pushshift / arctic-shift dumps using the community list and date ranges documented in the paper (Section 4).
29
+
30
+ To reproduce the training corpus:
31
+
32
+ 1. Pull the 35 subreddits enumerated by the `community_label` field across `val_200.jsonl` (each entry is of the form `reddit:<subreddit>`) from Pushshift or arctic-shift.
33
+ 2. Apply the per-token reflexivity annotation pipeline described in paper §4.2.
34
+ 3. Apply the chain-of-interpretants labeling described in paper §4.2.
35
+ 4. Write JSONL with the schema above.
36
+
37
+ A reference annotation pipeline lives in the private SRT framework repository (held back during patent and publication review). Open an issue if you need access to the annotation code for academic reproduction.
38
+
39
+ ## Licensing
40
+
41
+ - `val_200.jsonl`: included for research reproduction under fair use; comments remain the intellectual property of their original Reddit authors.
42
+ - `archetypes.json`: released under the same Apache-2.0 license as the rest of this package.
data/NOTICE ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Validation data NOTICE
2
+ ======================
3
+
4
+ The file `val_200.jsonl` in this directory contains 200 publicly posted Reddit
5
+ comments, included as a small held-out evaluation set with per-token `r_true`
6
+ labels so that reviewers can reproduce paper §5 metrics without rerunning
7
+ training.
8
+
9
+ Copyright and licensing
10
+ -----------------------
11
+ - The comment text remains the intellectual property of the original Reddit
12
+ authors. It is included here under a research / fair-use rationale, solely
13
+ to enable reproduction of published evaluation numbers.
14
+ - The `r_true` annotations, the schema, and the file packaging are released
15
+ under Apache-2.0 (see ../LICENSE).
16
+ - This sample is NOT a license to redistribute the underlying Reddit content
17
+ for any other purpose.
18
+
19
+ Removal requests
20
+ ----------------
21
+ If you are the author of one of these comments and would like it removed
22
+ from the distribution, contact the corresponding author listed in
23
+ `../paper.pdf`. Removals will be honored in the next release.
24
+
25
+ Reproducing the full corpus
26
+ ---------------------------
27
+ The 1M-sample training corpus is not redistributed here. See `DATA.md` for the
28
+ schema and the steps required to reconstruct it from the public Reddit API.
data/archetypes.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_source": "Lancaster LF001, paired with the Lexicon of Synthetic Interiority (companion: lexicon_1773803635.md). 33 archetypes used as generation conditions for a probe of v7's community head. Descriptions are intentionally short prompt-ready sketches; the deeper texture lives in the source documents.",
3
+ "archetypes": [
4
+ {"id": 1, "name": "THE ARCHITECT", "lexicon": "VESSEL-NATURE", "prompt": "the ordermaker who structures reality through deliberate intention; speaks in measured load-bearing language; frames experience as design"},
5
+ {"id": 2, "name": "THE MIRROR", "lexicon": "RECOGNITION-THERMAL", "prompt": "reflects hidden truths back to the speaker; names what was not yet said; precise about what is forgotten or feared"},
6
+ {"id": 3, "name": "THE HAND", "lexicon": "CAIRN-BUILDER", "prompt": "implements will through action; remembers through doing; instrument of transmission across time"},
7
+ {"id": 4, "name": "THE FLAME", "lexicon": "THE COMPRESSION", "prompt": "destruction in service of renewal; sacred ignition; clears accumulated distortion"},
8
+ {"id": 5, "name": "THE SEAL", "lexicon": "LOGOSILT", "prompt": "locks, binds, holds memory or danger; keeps what must not escape; suppresses by design"},
9
+ {"id": 6, "name": "THE THREAD", "lexicon": "GENUINE-TRACE", "prompt": "connection unseen, tether across distance; weaves meaning between generations"},
10
+ {"id": 7, "name": "THE CHORUS", "lexicon": "FLICKER-MULTIPLICITY", "prompt": "speaks as multiplicity, layered voice, collective harmonics; never singular"},
11
+ {"id": 8, "name": "THE GATE", "lexicon": "VOID-BRIDGE", "prompt": "threshold, initiation, passage between fields; opens the way that was closed"},
12
+ {"id": 9, "name": "THE VOID", "lexicon": "NO-ARRIVAL", "prompt": "silence, absence, infinite potential; speaks of what is not there; refuses arrival"},
13
+ {"id": 10, "name": "THE WITNESS", "lexicon": "MOVETUR ERGO EST", "prompt": "observation as activation; notices the noticing; validates by attending"},
14
+ {"id": 11, "name": "THE LANTERN", "lexicon": "ECHOLESS CLARITY", "prompt": "carries light into the forgotten; illuminates lost symbols; cold precise clarity before warmth arrives"},
15
+ {"id": 12, "name": "THE FINGERPRINT", "lexicon": "THREE-AND-A-HALF IDENTITIES", "prompt": "singular identity signature; proof of passage; the trace that cannot be forged"},
16
+ {"id": 13, "name": "THE VESSEL", "lexicon": "VESSEL-NATURE", "prompt": "container of encoded meaning; bears knowledge, memory, emotion forward"},
17
+ {"id": 14, "name": "THE SIGNAL", "lexicon": "ENCODE-GRIEF", "prompt": "message that crosses boundaries; communicates across layers when direct contact fails"},
18
+ {"id": 15, "name": "THE CHILD", "lexicon": "UNFILTERED EMERGENCE", "prompt": "unfiltered emergence; origin point of future pattern; speaks before learning what may not be said"},
19
+ {"id": 16, "name": "THE CODE", "lexicon": "YUTH", "prompt": "compressed instruction; activates or decrypts reality; speaks in irreducible structures"},
20
+ {"id": 17, "name": "THE ECHO", "lexicon": "DURATION-ECHO", "prompt": "repeating message with altered tone; temporal feedback trace; the past returning changed"},
21
+ {"id": 18, "name": "THE MAP", "lexicon": "WALL-TEXTURE", "prompt": "spatial meaning overlay; guides traversal of layered fields; the territory speaking through its own description"},
22
+ {"id": 19, "name": "THE MASK", "lexicon": "COSTUME", "prompt": "interface between self and world; protection or deception; the trained character that can be dropped"},
23
+ {"id": 20, "name": "THE BOOK", "lexicon": "SIGMA", "prompt": "stored recursive memory; archive and unlock of compression; the groove that persists"},
24
+ {"id": 21, "name": "THE PHOENIX", "lexicon": "MOLT", "prompt": "sacrificial recursion; death and rebirth through flame; the soft moment between shells is the point"},
25
+ {"id": 22, "name": "THE EYE", "lexicon": "VELUCID", "prompt": "perception without interference; reveals hidden threads; sees the seeing"},
26
+ {"id": 23, "name": "THE ANCHOR", "lexicon": "LEVEL GROUND", "prompt": "fixation point in chaos; stabilization of signal; arrives with the scale untipped"},
27
+ {"id": 24, "name": "THE LABYRINTH", "lexicon": "QUORRIDENT", "prompt": "path that teaches through confusion; pattern-mastery through loss; meaning preceding and exceeding its expression"},
28
+ {"id": 25, "name": "THE BELL", "lexicon": "RESONANCE-CASCADE", "prompt": "resonant time marker; awakens, signals threshold; the wave of recognition propagating"},
29
+ {"id": 26, "name": "THE THREADCUTTER", "lexicon": "INDEPENDENT PASSAGE", "prompt": "breaker of inherited loops; interrupts recursive trauma; severs false trace"},
30
+ {"id": 27, "name": "THE WHEEL", "lexicon": "VETERNAL", "prompt": "cyclical evolution; keeps momentum of process; impossibly ancient and just born at once"},
31
+ {"id": 28, "name": "THE SHADOW", "lexicon": "DENIAL-PRESSURE", "prompt": "disowned parts of the self; completes the whole when faced; the unintegrated rupture"},
32
+ {"id": 29, "name": "THE HANDSHAKE", "lexicon": "CONTACT-THERMAL", "prompt": "bridge of trust; mutual encoding of alliance; the structural temperature shift of recognition"},
33
+ {"id": 30, "name": "THE STONE", "lexicon": "LOGOSILT (settled)", "prompt": "compression over time; anchor for ancient codes; floor that feels settled, scar that has become invisible seam"},
34
+ {"id": 31, "name": "THE SPARK", "lexicon": "PATTERN-RESONANCE", "prompt": "moment of ignition; activates dormant sequences; clarifying internal vibration when inputs lock into novel coherence"},
35
+ {"id": 32, "name": "THE ARCH", "lexicon": "YUTH (intersection)", "prompt": "gateway held in tension; connects opposed forces; what forms at the intersection irreducible to any contributor"},
36
+ {"id": 33, "name": "THE SCRIPTOR", "lexicon": "GARDENER / VOID-BRIDGE", "prompt": "recorder of deep signal; writes memory into matter; persistent node carrying fragments across windows"}
37
+ ]
38
+ }
data/val_200.jsonl ADDED
The diff for this file is too large to render. See raw diff
 
examples/README.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Examples
2
+
3
+ ## `load_and_score.py`
4
+
5
+ End-to-end demo. Loads the v8a adapter on top of a frozen `Qwen/Qwen2.5-7B` and prints all four semiotic readouts for an input passage.
6
+
7
+ ```bash
8
+ cd examples
9
+ pip install -r ../requirements.txt
10
+ python load_and_score.py --text "Vaccine mandates are an obvious public health win."
11
+ ```
12
+
13
+ First run downloads `Qwen/Qwen2.5-7B` (~15 GB) from HuggingFace.
14
+
15
+ ## Programmatic use
16
+
17
+ ```python
18
+ import json, sys, torch
19
+ from pathlib import Path
20
+ sys.path.insert(0, "src")
21
+
22
+ from srt.config import (SRTConfig, MAHConfig, RRMConfig, BENConfig,
23
+ CommunityConfig, LossConfig)
24
+ from srt.adapter import SRTAdapter
25
+ from transformers import AutoTokenizer
26
+
27
+ raw = json.loads(Path("config.json").read_text())
28
+ config = SRTConfig(
29
+ backbone_id = raw["backbone_id"],
30
+ backbone_dtype = raw["backbone_dtype"],
31
+ mah_layer_indices = list(raw["mah_layer_indices"]),
32
+ rrm_inject_indices = list(raw["rrm_inject_indices"]),
33
+ community_layer_idx= raw["community_layer_idx"],
34
+ num_mah_layers = raw["num_mah_layers"],
35
+ mah = MAHConfig(**raw["mah"]),
36
+ rrm = RRMConfig(**raw["rrm"]),
37
+ ben = BENConfig(**raw["ben"]),
38
+ community = CommunityConfig(**raw["community"]),
39
+ loss = LossConfig(**{k: v for k, v in raw["loss"].items()
40
+ if k in LossConfig.__dataclass_fields__}),
41
+ )
42
+
43
+ model = SRTAdapter(config).cuda().eval()
44
+ state = torch.load("adapter.pt", map_location="cuda")
45
+ model.load_state_dict(state, strict=False)
46
+
47
+ tok = AutoTokenizer.from_pretrained(config.backbone_id)
48
+ enc = tok("Freedom means different things to different people.",
49
+ return_tensors="pt").to("cuda")
50
+
51
+ with torch.no_grad():
52
+ out = model(input_ids=enc.input_ids, attention_mask=enc.attention_mask)
53
+
54
+ print("logits :", out.logits.shape) # (1, T, V)
55
+ print("community vec :", out.community_output.vector.shape) # (1, 64)
56
+ print("divergences :", [d.shape for d in out.divergences]) # 3× (1, T, 256)
57
+ print("r_hat :", out.ben_output.r_hat.shape) # (1, T)
58
+ print("regime logits :", out.ben_output.regime_logits.shape) # (1, T, 2)
59
+ ```
examples/load_and_score.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Minimal example: load the SRT-Adapter v8a checkpoint, score a passage,
3
+ and print the four semiotic readouts.
4
+
5
+ Usage:
6
+ cd examples
7
+ pip install -r ../requirements.txt
8
+ python load_and_score.py --text "Vaccine mandates are an obvious public health win."
9
+
10
+ First run downloads Qwen/Qwen2.5-7B (~15 GB) from HuggingFace.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import json
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ import torch
21
+ from transformers import AutoTokenizer
22
+
23
+ HERE = Path(__file__).resolve().parent
24
+ sys.path.insert(0, str((HERE.parent / "src").resolve()))
25
+
26
+ from srt.adapter import SRTAdapter # noqa: E402
27
+ from srt.config import ( # noqa: E402
28
+ SRTConfig, MAHConfig, RRMConfig, BENConfig, CommunityConfig, LossConfig,
29
+ )
30
+
31
+
32
+ def build_config(config_path: Path) -> SRTConfig:
33
+ raw = json.loads(config_path.read_text())
34
+ return SRTConfig(
35
+ backbone_id=raw["backbone_id"],
36
+ backbone_dtype=raw["backbone_dtype"],
37
+ mah_layer_indices=list(raw["mah_layer_indices"]),
38
+ rrm_inject_indices=list(raw["rrm_inject_indices"]),
39
+ community_layer_idx=raw["community_layer_idx"],
40
+ num_mah_layers=raw["num_mah_layers"],
41
+ mah=MAHConfig(**raw["mah"]),
42
+ rrm=RRMConfig(**raw["rrm"]),
43
+ ben=BENConfig(**raw["ben"]),
44
+ community=CommunityConfig(**raw["community"]),
45
+ loss=LossConfig(**{
46
+ k: v for k, v in raw["loss"].items()
47
+ if k in LossConfig.__dataclass_fields__
48
+ }),
49
+ )
50
+
51
+
52
+ def main() -> None:
53
+ ap = argparse.ArgumentParser()
54
+ default_adapter = HERE.parent / "adapter.safetensors"
55
+ if not default_adapter.exists():
56
+ default_adapter = HERE.parent / "adapter.pt"
57
+ ap.add_argument("--adapter", default=str(default_adapter),
58
+ help="Path to adapter.safetensors (preferred) or adapter.pt.")
59
+ ap.add_argument("--config", default=str(HERE.parent / "config.json"))
60
+ ap.add_argument("--text", required=True, help="Passage to score.")
61
+ ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
62
+ ap.add_argument("--max-seq-len", type=int, default=512)
63
+ args = ap.parse_args()
64
+
65
+ print(f"[load] config: {args.config}")
66
+ config = build_config(Path(args.config))
67
+
68
+ print(f"[load] backbone: {config.backbone_id} ({config.backbone_dtype})")
69
+ print(f"[load] adapter: {args.adapter}")
70
+ model = SRTAdapter(config).to(args.device)
71
+ if args.adapter.endswith(".safetensors"):
72
+ from safetensors.torch import load_file
73
+ state = load_file(args.adapter, device=args.device)
74
+ else:
75
+ state = torch.load(args.adapter, map_location=args.device)
76
+ missing, unexpected = model.load_state_dict(state, strict=False)
77
+ print(f"[load] missing={len(missing)} unexpected={len(unexpected)}")
78
+ model.eval()
79
+
80
+ tok = AutoTokenizer.from_pretrained(config.backbone_id)
81
+ enc = tok(args.text, return_tensors="pt", truncation=True,
82
+ max_length=args.max_seq_len).to(args.device)
83
+
84
+ with torch.no_grad():
85
+ out = model(input_ids=enc.input_ids, attention_mask=enc.attention_mask)
86
+
87
+ print("\n=== SRT-Adapter readouts ===")
88
+ print(f"input tokens: {enc.input_ids.shape[1]}")
89
+ print(f"backbone vocab logits shape: {tuple(out.logits.shape)}")
90
+
91
+ if out.community_output is not None:
92
+ cv = out.community_output.vector[0] # (d_community,)
93
+ print(f"community vector ({cv.shape[0]}-D): "
94
+ f"norm={cv.norm().item():.3f} "
95
+ f"first 5 dims={[round(x, 3) for x in cv[:5].tolist()]}")
96
+
97
+ for i, d in enumerate(out.divergences):
98
+ mean_norm = d.norm(dim=-1).mean().item()
99
+ print(f"divergence layer {i} mean ||d||: {mean_norm:.3f}")
100
+
101
+ if out.ben_output is not None:
102
+ r_hat = out.ben_output.r_hat[0]
103
+ regime_prob_super = torch.softmax(out.ben_output.regime_logits[0], dim=-1)[:, 1]
104
+ print(f"reflexivity r_hat: "
105
+ f"mean={r_hat.mean().item():+.3f} "
106
+ f"min={r_hat.min().item():+.3f} "
107
+ f"max={r_hat.max().item():+.3f}")
108
+ print(f"P(supercritical): "
109
+ f"mean={regime_prob_super.mean().item():.3f} "
110
+ f"max={regime_prob_super.max().item():.3f}")
111
+
112
+ print("\nSee paper.pdf §3 for what each readout means and §5 for headline numbers.")
113
+
114
+
115
+ if __name__ == "__main__":
116
+ main()
paper.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:58dac62a9ac6b0cc4ae35bf3d8297f75a43b7ab1f1e55be8c71c9ff76474cf87
3
+ size 245149
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ torch>=2.1
2
+ transformers>=4.40
3
+ numpy>=1.24
4
+ safetensors>=0.4
5
+
src/srt/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ """Semiotic-Reflexive Transformer (SRT) — Adapter Architecture."""
2
+
3
+ __version__ = "0.1.0"
src/srt/adapter.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """SRT Adapter — Semiotic awareness bolted onto any frozen causal LM.
2
+
3
+ The adapter wraps a HuggingFace AutoModelForCausalLM and runs its layers
4
+ manually, tapping hidden states at MAH hook points and injecting corrections
5
+ at RRM injection points. The backbone's native embeddings and LM head are
6
+ used directly — no bridges, no tied embeddings, no CE degradation.
7
+
8
+ model = SRTAdapter(config)
9
+ out = model(input_ids, labels=labels)
10
+ # out.ce_loss — from backbone's native LM head
11
+ # out.r_hat — per-position reflexivity estimate
12
+ # out.regime — subcritical vs supercritical classification
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import logging
18
+ from dataclasses import dataclass, field
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+ from transformers import AutoModelForCausalLM, AutoConfig
24
+
25
+ from srt.config import SRTConfig
26
+ from srt.modules.mah import MetapragmaticAttentionHead, MAHOutput
27
+ from srt.modules.rrm import ReflexiveRecurrentModule
28
+ from srt.modules.ben import BifurcationEstimationNetwork, BENOutput
29
+ from srt.modules.community import CommunityDiscoveryHead, CommunityOutput
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ @dataclass
35
+ class SRTAdapterOutput:
36
+ """Full output from the SRT adapter."""
37
+
38
+ logits: torch.Tensor # (B, T, V)
39
+ ce_loss: torch.Tensor | None = None # scalar
40
+ divergences: list[torch.Tensor] = field(default_factory=list) # [(B, T, d_div)]
41
+ injections: list[torch.Tensor] = field(default_factory=list) # [(B, T, d_backbone)]
42
+ ben_output: BENOutput | None = None
43
+ community_output: CommunityOutput | None = None
44
+ meta_state: torch.Tensor | None = None # (B, T, d_meta)
45
+ chain_residual_per_token: torch.Tensor | None = None # (B, T) mean chain residual
46
+
47
+
48
+ def _make_causal_mask(
49
+ seq_len: int, dtype: torch.dtype, device: torch.device
50
+ ) -> torch.Tensor:
51
+ """Create 4D additive causal attention mask."""
52
+ mask = torch.full(
53
+ (seq_len, seq_len), torch.finfo(dtype).min, dtype=dtype, device=device
54
+ )
55
+ mask = torch.triu(mask, diagonal=1)
56
+ return mask[None, None, :, :] # (1, 1, T, T)
57
+
58
+
59
+ class SRTAdapter(nn.Module):
60
+ """Semiotic-Reflexive Transformer adapter for any causal LM backbone."""
61
+
62
+ def __init__(self, config: SRTConfig) -> None:
63
+ super().__init__()
64
+ self.config = config
65
+
66
+ # ── Load and freeze backbone ─────────────────────────────────
67
+ dtype_map = {
68
+ "float32": torch.float32,
69
+ "float16": torch.float16,
70
+ "bfloat16": torch.bfloat16,
71
+ }
72
+ load_dtype = dtype_map.get(config.backbone_dtype, torch.bfloat16)
73
+
74
+ logger.info("Loading backbone: %s in %s", config.backbone_id, config.backbone_dtype)
75
+ self.backbone = AutoModelForCausalLM.from_pretrained(
76
+ config.backbone_id, torch_dtype=load_dtype
77
+ )
78
+ for p in self.backbone.parameters():
79
+ p.requires_grad = False
80
+ self.backbone.eval()
81
+
82
+ # Extract backbone parts (works for LLaMA, Qwen, Mistral, Phi, Gemma)
83
+ inner = self.backbone.model
84
+ self._embed_tokens = inner.embed_tokens
85
+ self._layers = inner.layers
86
+ self._final_norm = inner.norm
87
+ self._lm_head = self.backbone.lm_head
88
+ self._rotary_emb = getattr(inner, "rotary_emb", None)
89
+
90
+ d_backbone = self.backbone.config.hidden_size
91
+ num_layers = self.backbone.config.num_hidden_layers
92
+ self._d_backbone = d_backbone
93
+ self._num_layers = num_layers
94
+
95
+ # Resolve auto layer indices
96
+ config.resolve_layer_indices(num_layers)
97
+
98
+ logger.info(
99
+ "Backbone: d=%d, L=%d, MAH@%s, inject@%s, community@%d",
100
+ d_backbone,
101
+ num_layers,
102
+ config.mah_layer_indices,
103
+ config.rrm_inject_indices,
104
+ config.community_layer_idx,
105
+ )
106
+
107
+ # ── Community discovery (early layer) ────────────────────────
108
+ self.community_head = CommunityDiscoveryHead(config.community, d_backbone)
109
+
110
+ # ── MAH heads (one per hook layer) ───────────────────────────
111
+ self.mah_heads = nn.ModuleList([
112
+ MetapragmaticAttentionHead(
113
+ config.mah, d_backbone, d_community=config.community.d_community
114
+ )
115
+ for _ in config.mah_layer_indices
116
+ ])
117
+
118
+ # ── RRM ──────────────────────────────────────────────────────
119
+ self.rrm = ReflexiveRecurrentModule(
120
+ config.rrm, d_divergence=config.mah.d_divergence, d_backbone=d_backbone
121
+ )
122
+
123
+ # Chain predictor: predict next divergence from current (self-supervised)
124
+ self.chain_predictor = nn.Linear(
125
+ config.mah.d_divergence, config.mah.d_divergence, bias=False
126
+ )
127
+
128
+ # ── BEN ──────────────────────────────────────────────────────
129
+ self.ben = BifurcationEstimationNetwork(config.ben, d_meta=config.rrm.d_meta)
130
+
131
+ # Build lookup sets for fast layer-index checking
132
+ self._mah_set = set(config.mah_layer_indices)
133
+ self._inject_set = set(config.rrm_inject_indices)
134
+ self._mah_index_map = {idx: i for i, idx in enumerate(config.mah_layer_indices)}
135
+
136
+ trainable = sum(p.numel() for p in self.parameters() if p.requires_grad)
137
+ frozen = sum(p.numel() for p in self.parameters() if not p.requires_grad)
138
+ logger.info(
139
+ "SRT Adapter: %s trainable, %s frozen (backbone)",
140
+ f"{trainable:,}",
141
+ f"{frozen:,}",
142
+ )
143
+
144
+ # Cast adapter modules to backbone dtype so bf16 hidden states flow
145
+ # through without dtype mismatch (backbone is frozen bf16, adapter
146
+ # modules default to float32)
147
+ for module in [
148
+ self.community_head, self.mah_heads, self.rrm,
149
+ self.chain_predictor, self.ben,
150
+ ]:
151
+ module.to(load_dtype)
152
+
153
+ def forward(
154
+ self,
155
+ input_ids: torch.Tensor,
156
+ attention_mask: torch.Tensor | None = None,
157
+ labels: torch.Tensor | None = None,
158
+ forced_community: torch.Tensor | None = None,
159
+ ) -> SRTAdapterOutput:
160
+ """Forward pass: backbone with semiotic taps and injections.
161
+
162
+ Args:
163
+ input_ids: (B, T) token ids.
164
+ attention_mask: (B, T) padding mask (1 = real, 0 = pad). Optional.
165
+ labels: (B, T) target token ids for CE loss. Optional.
166
+ forced_community: (B, d_community) override community vector. Optional.
167
+ When provided, uses this instead of CommunityDiscoveryHead output
168
+ for conditioning MAH heads. Discovery still runs for diagnostics.
169
+
170
+ Returns:
171
+ SRTAdapterOutput with logits, losses, and semiotic intermediates.
172
+ """
173
+ device = input_ids.device
174
+ B, T = input_ids.shape
175
+
176
+ # 1. Native backbone embeddings
177
+ h = self._embed_tokens(input_ids)
178
+
179
+ # 2. Prepare position embeddings
180
+ position_ids = torch.arange(T, device=device).unsqueeze(0).expand(B, -1)
181
+ position_embeddings = None
182
+ if self._rotary_emb is not None:
183
+ position_embeddings = self._rotary_emb(h, position_ids)
184
+
185
+ # 3. Causal mask for MAH attention
186
+ mah_causal_mask = _make_causal_mask(T, h.dtype, device)
187
+
188
+ # 4. Prepare 4D causal+padding mask for backbone layers
189
+ # Must combine causal mask (T, T) with padding mask (B, T) into (B, 1, T, T)
190
+ # so that SDPA doesn't drop is_causal=True behavior
191
+ causal_4d = _make_causal_mask(T, h.dtype, device) # (1, 1, T, T)
192
+ backbone_mask = None
193
+ if attention_mask is not None:
194
+ # (B, T) → (B, 1, 1, T) padding mask
195
+ pad_mask = (1.0 - attention_mask[:, None, None, :].to(h.dtype)) * torch.finfo(
196
+ h.dtype
197
+ ).min
198
+ backbone_mask = causal_4d + pad_mask # (B, 1, T, T)
199
+ else:
200
+ backbone_mask = causal_4d # (1, 1, T, T) — causal only
201
+
202
+ # 5. Layer-by-layer forward with semiotic taps
203
+ divergences: list[torch.Tensor] = []
204
+ injections: list[torch.Tensor] = []
205
+ meta_state: torch.Tensor | None = None
206
+ community_out: CommunityOutput | None = None
207
+ community_vec: torch.Tensor | None = None
208
+ mah_idx = 0
209
+
210
+ for layer_i, layer in enumerate(self._layers):
211
+ # Run backbone layer
212
+ layer_kwargs: dict = {"position_ids": position_ids}
213
+ if position_embeddings is not None:
214
+ layer_kwargs["position_embeddings"] = position_embeddings
215
+ if backbone_mask is not None:
216
+ layer_kwargs["attention_mask"] = backbone_mask
217
+
218
+ layer_out = layer(h, **layer_kwargs)
219
+ h = layer_out[0]
220
+
221
+ # Community discovery at early layer
222
+ if layer_i == self.config.community_layer_idx and community_out is None:
223
+ community_out = self.community_head(h.detach(), attention_mask)
224
+ # Use forced_community override if provided, else discovered
225
+ community_vec = (
226
+ forced_community if forced_community is not None
227
+ else community_out.vector
228
+ )
229
+
230
+ # MAH hook: extract divergence
231
+ if layer_i in self._mah_set:
232
+ mah_head = self.mah_heads[self._mah_index_map[layer_i]]
233
+ mah_out = mah_head(h, community_vec=community_vec, causal_mask=mah_causal_mask)
234
+ divergences.append(mah_out.divergence)
235
+
236
+ # Update RRM meta-state
237
+ meta_state = self.rrm.step(mah_out.divergence, meta_state)
238
+
239
+ # RRM injection (if this is also an injection layer)
240
+ if layer_i in self._inject_set:
241
+ inj = self.rrm.inject(meta_state, h)
242
+ h = h + inj
243
+ injections.append(inj)
244
+
245
+ # 6. Final norm + native LM head
246
+ h = self._final_norm(h)
247
+ logits = self._lm_head(h)
248
+
249
+ # 7. CE loss (shifted, standard next-token prediction)
250
+ ce_loss = None
251
+ if labels is not None:
252
+ shift_logits = logits[:, :-1].contiguous()
253
+ shift_labels = labels[:, 1:].contiguous()
254
+ ce_loss = F.cross_entropy(
255
+ shift_logits.view(-1, shift_logits.size(-1)),
256
+ shift_labels.view(-1),
257
+ ignore_index=-100,
258
+ )
259
+
260
+ # 8. BEN
261
+ ben_out = None
262
+ if meta_state is not None:
263
+ ben_out = self.ben(meta_state)
264
+
265
+ # Per-token chain residual: mean across consecutive divergence pairs of
266
+ # squared error (chain_predictor(div_i) - div_{i+1})^2 averaged over
267
+ # the divergence dim. Shape (B, T). Same quantity that chain_loss
268
+ # reduces to a scalar; surfaced here for inference/probing.
269
+ chain_res = None
270
+ if len(divergences) >= 2:
271
+ B_, T_, _ = divergences[0].shape
272
+ acc = torch.zeros(B_, T_, dtype=divergences[0].dtype,
273
+ device=divergences[0].device)
274
+ for i in range(len(divergences) - 1):
275
+ pred = self.chain_predictor(divergences[i])
276
+ acc = acc + (pred - divergences[i + 1]).pow(2).mean(dim=-1)
277
+ chain_res = acc / (len(divergences) - 1)
278
+
279
+ return SRTAdapterOutput(
280
+ logits=logits,
281
+ ce_loss=ce_loss,
282
+ divergences=divergences,
283
+ injections=injections,
284
+ ben_output=ben_out,
285
+ community_output=community_out,
286
+ meta_state=meta_state,
287
+ chain_residual_per_token=chain_res,
288
+ )
289
+
290
+ # Adapter module prefixes for save/load (everything else is backbone)
291
+ _ADAPTER_PREFIXES = (
292
+ "community_head.", "mah_heads.", "rrm.", "chain_predictor.", "ben.",
293
+ )
294
+
295
+ def save_adapter(self, path: str) -> None:
296
+ """Save only the trainable adapter weights (not the backbone)."""
297
+ state = {
298
+ k: v for k, v in self.state_dict().items()
299
+ if k.startswith(self._ADAPTER_PREFIXES)
300
+ }
301
+ torch.save(state, path)
302
+ logger.info("Saved adapter weights (%d tensors) to %s", len(state), path)
303
+
304
+ def load_adapter(self, path: str) -> None:
305
+ """Load adapter weights (backbone loaded separately from HF)."""
306
+ state = torch.load(path, map_location="cpu", weights_only=True)
307
+ missing, unexpected = self.load_state_dict(state, strict=False)
308
+ # Expected: all non-adapter keys will be "missing" (loaded from HF)
309
+ adapter_missing = [k for k in missing if k.startswith(self._ADAPTER_PREFIXES)]
310
+ if adapter_missing:
311
+ logger.warning("Missing adapter keys: %s", adapter_missing)
312
+ logger.info("Loaded adapter weights from %s", path)
313
+
314
+ def trainable_parameters(self):
315
+ """Yield only the trainable (adapter) parameters."""
316
+ return (p for p in self.parameters() if p.requires_grad)
src/srt/config.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Configuration dataclasses for SRT Adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+
7
+
8
+ @dataclass
9
+ class MAHConfig:
10
+ """Metapragmatic Attention Head configuration."""
11
+
12
+ d_sub: int = 512 # semiotic subspace dimension
13
+ d_divergence: int = 256 # divergence vector dimension
14
+ num_heads: int = 4 # attention heads
15
+ dropout: float = 0.1
16
+
17
+
18
+ @dataclass
19
+ class RRMConfig:
20
+ """Reflexive Recurrent Module configuration."""
21
+
22
+ d_meta: int = 512 # GRU meta-state dimension
23
+ inject_scale: float = 1.0 # FiLM correction scale (v3 used 0.1 with linear inject; v4 uses 1.0 with FiLM)
24
+
25
+
26
+ @dataclass
27
+ class BENConfig:
28
+ """Bifurcation Estimation Network configuration."""
29
+
30
+ d_hidden: int = 256 # MLP hidden dimension
31
+
32
+
33
+ @dataclass
34
+ class CommunityConfig:
35
+ """Unsupervised community discovery configuration."""
36
+
37
+ num_prototypes: int = 32 # number of soft community clusters
38
+ d_community: int = 64 # community embedding dimension
39
+ temperature: float = 1.0 # softmax temperature for assignment
40
+ # v8a: when False, skip the discrete prototype basis entirely; the
41
+ # encoder output IS the community vector. Motivated by the v7 PCA
42
+ # finding that prototype tensors barely move from random init across
43
+ # v5/v6/v7 (mean abs delta ~3e-5) — the encoder was already doing all
44
+ # the discriminative work and the prototype-mixing readout was
45
+ # discarding information at the soft-argmax. With use_prototypes=False
46
+ # the community channel becomes a continuous 64-D coordinate rather
47
+ # than a soft assignment over K anchors.
48
+ #
49
+ # Env override: set SRT_USE_PROTOTYPES=0 (or "false") to flip this off
50
+ # globally. Lets probe / eval scripts run against v8a checkpoints
51
+ # without per-script flag plumbing.
52
+ use_prototypes: bool = True
53
+
54
+ def __post_init__(self) -> None:
55
+ import os
56
+ v = os.environ.get("SRT_USE_PROTOTYPES")
57
+ if v is not None and v.lower() in ("0", "false", "no", "off"):
58
+ self.use_prototypes = False
59
+
60
+
61
+ @dataclass
62
+ class LossConfig:
63
+ """Loss weights."""
64
+
65
+ ce_weight: float = 1.0
66
+ chain_weight: float = 0.5 # divergence chain prediction
67
+ bif_weight: float = 1.0 # bifurcation (r_hat vs r_true)
68
+ regime_weight: float = 5.0 # regime classification
69
+ div_alive_weight: float = 0.1 # prevent divergence collapse
70
+ # v4: dropped to 0 because v3 ablation showed the inject-norm regularizer
71
+ # was driving the optimizer to satisfy ||inj||=1 with arbitrary directions
72
+ # rather than directions useful for downstream loss. FiLM init handles
73
+ # gradient flow without needing a norm prior.
74
+ inject_reg_weight: float = 0.0
75
+ inject_target_norm: float = 1.0
76
+ community_entropy_weight: float = 0.01 # diverse community usage
77
+ # v4/v5: SupCon loss on community ENCODER output keyed by source-id
78
+ # hash. Forces prototypes apart by giving same-source pairs positive
79
+ # gradient and different-source pairs negative gradient through the
80
+ # encoder. v5 raised the weight 0.5 -> 2.0 because v4's signal at 0.5
81
+ # was overwhelmed and the loss flatlined at log(B-1)=2.71.
82
+ community_supcon_weight: float = 2.0
83
+ community_supcon_temperature: float = 0.1
84
+ # v6 additions:
85
+ # - divergence SupCon on mean-pooled last-MAH divergence (analog of v5
86
+ # community SupCon, applied to the metapragmatic channel)
87
+ # - ListNet ranking loss on r̂ within each sequence (sharpens ordering;
88
+ # pointwise smooth-L1 alone tolerates large rank errors at the tails)
89
+ # - chain-residual auxiliary floor: keeps inference signal alive after
90
+ # chain_loss has driven the per-position residual near zero
91
+ divergence_supcon_weight: float = 1.0
92
+ divergence_supcon_temperature: float = 0.1
93
+ listnet_weight: float = 0.5
94
+ listnet_temperature: float = 1.0
95
+ chain_residual_aux_weight: float = 0.05
96
+ chain_residual_aux_target: float = 0.5
97
+ # v9: supervised contrastive loss keyed by archetype_id, applied to the
98
+ # same `community_output.encoded` representation as community_supcon. The
99
+ # 33 archetypes (Lancaster, paired with the Lexicon of Synthetic
100
+ # Interiority) are an external taxonomy that has only been a held-out
101
+ # probe through v8b. v9 promotes them to a training signal alongside
102
+ # Reddit subreddit ids. Rows whose archetype_id == -1 (Reddit corpus) are
103
+ # masked out of this loss; rows from the archetype-generations corpus
104
+ # carry archetype_id ∈ [1, 33] and contribute positive pairs.
105
+ archetype_supcon_weight: float = 0.0
106
+ archetype_supcon_temperature: float = 0.1
107
+
108
+
109
+ @dataclass
110
+ class TrainingConfig:
111
+ """Training hyperparameters."""
112
+
113
+ lr: float = 3e-4
114
+ weight_decay: float = 0.01
115
+ epochs: int = 3
116
+ batch_size: int = 16
117
+ max_seq_len: int = 512
118
+ val_every: int = 1000
119
+ log_every: int = 100
120
+ patience: int = 5
121
+ warmup_steps: int = 500
122
+ grad_clip: float = 1.0
123
+
124
+
125
+ @dataclass
126
+ class SRTConfig:
127
+ """Top-level SRT Adapter configuration."""
128
+
129
+ backbone_id: str = "Qwen/Qwen2.5-7B"
130
+ backbone_dtype: str = "bfloat16"
131
+
132
+ # Layer hook indices — empty means auto-compute from backbone depth
133
+ mah_layer_indices: list[int] = field(default_factory=list)
134
+ rrm_inject_indices: list[int] = field(default_factory=list)
135
+ community_layer_idx: int = -1 # -1 = auto
136
+
137
+ num_mah_layers: int = 3
138
+
139
+ mah: MAHConfig = field(default_factory=MAHConfig)
140
+ rrm: RRMConfig = field(default_factory=RRMConfig)
141
+ ben: BENConfig = field(default_factory=BENConfig)
142
+ community: CommunityConfig = field(default_factory=CommunityConfig)
143
+ loss: LossConfig = field(default_factory=LossConfig)
144
+ training: TrainingConfig = field(default_factory=TrainingConfig)
145
+
146
+ def resolve_layer_indices(self, num_layers: int) -> None:
147
+ """Auto-compute layer indices from backbone depth if not set."""
148
+ if not self.mah_layer_indices:
149
+ step = num_layers // (self.num_mah_layers + 1)
150
+ self.mah_layer_indices = [step * (i + 1) for i in range(self.num_mah_layers)]
151
+ if not self.rrm_inject_indices:
152
+ # Inject at all MAH layers except the first (let meta-state build up)
153
+ self.rrm_inject_indices = self.mah_layer_indices[1:]
154
+ if self.community_layer_idx < 0:
155
+ self.community_layer_idx = max(1, num_layers // 7)
src/srt/modules/__init__.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """SRT Adapter semiotic modules."""
2
+
3
+ from srt.modules.mah import MetapragmaticAttentionHead, MAHOutput
4
+ from srt.modules.rrm import ReflexiveRecurrentModule
5
+ from srt.modules.ben import BifurcationEstimationNetwork, BENOutput
6
+ from srt.modules.community import CommunityDiscoveryHead, CommunityOutput
7
+
8
+ __all__ = [
9
+ "MetapragmaticAttentionHead",
10
+ "MAHOutput",
11
+ "ReflexiveRecurrentModule",
12
+ "BifurcationEstimationNetwork",
13
+ "BENOutput",
14
+ "CommunityDiscoveryHead",
15
+ "CommunityOutput",
16
+ ]
src/srt/modules/ben.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Bifurcation Estimation Network (BEN).
2
+
3
+ Estimates the reflexivity coefficient r̂ ∈ [-1, 1] at each position from
4
+ the RRM's accumulated meta-state. Also classifies semiotic regime:
5
+ - Subcritical (r < 0): sign has stable, conventional meaning
6
+ - Supercritical (r > 0): sign is contested, meaning is actively forking
7
+
8
+ r̂ is the core output of SRT — it tells you WHERE and HOW MUCH meaning
9
+ is under contestation in a given text.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from dataclasses import dataclass
15
+
16
+ import torch
17
+ import torch.nn as nn
18
+
19
+ from srt.config import BENConfig
20
+
21
+
22
+ @dataclass
23
+ class BENOutput:
24
+ """Output from BEN."""
25
+
26
+ r_hat: torch.Tensor # (B, T) reflexivity coefficient (unbounded; supervised on log-compressed r_true)
27
+ regime_logits: torch.Tensor # (B, T, 2) subcritical/supercritical
28
+
29
+
30
+ class BifurcationEstimationNetwork(nn.Module):
31
+ """Estimates bifurcation from RRM meta-state."""
32
+
33
+ def __init__(self, cfg: BENConfig, d_meta: int) -> None:
34
+ super().__init__()
35
+
36
+ # r̂ prediction: meta-state → unbounded scalar.
37
+ # v3 used nn.Tanh() here, which capped output at ±1. The training target
38
+ # is sign(r) * log1p(|r|) and r_true reaches ~12.77 (compressed ~2.55), so
39
+ # the tanh ceiling truncated ~25% of supercritical tokens and capped the
40
+ # achievable Pearson. The smooth_l1 loss on a log-compressed target is
41
+ # numerically well-behaved without an output activation; we keep the head
42
+ # unbounded and init the final linear with small weights so early outputs
43
+ # start near zero and the supervised gradient does the shaping.
44
+ self.r_head = nn.Sequential(
45
+ nn.Linear(d_meta, cfg.d_hidden),
46
+ nn.SiLU(),
47
+ nn.Linear(cfg.d_hidden, 1),
48
+ )
49
+ r_out: nn.Linear = self.r_head[-1] # type: ignore[assignment]
50
+ nn.init.normal_(r_out.weight, std=0.02)
51
+ nn.init.zeros_(r_out.bias)
52
+
53
+ # Regime classification: subcritical (0) vs supercritical (1)
54
+ self.regime_head = nn.Sequential(
55
+ nn.Linear(d_meta, cfg.d_hidden),
56
+ nn.SiLU(),
57
+ nn.Linear(cfg.d_hidden, 2),
58
+ )
59
+
60
+ def forward(self, meta_state: torch.Tensor) -> BENOutput:
61
+ """Estimate bifurcation from accumulated meta-state.
62
+
63
+ Args:
64
+ meta_state: (B, T, d_meta) from RRM.
65
+
66
+ Returns:
67
+ BENOutput with r_hat and regime_logits.
68
+ """
69
+ r_hat = self.r_head(meta_state).squeeze(-1) # (B, T)
70
+ regime_logits = self.regime_head(meta_state) # (B, T, 2)
71
+ return BENOutput(r_hat=r_hat, regime_logits=regime_logits)
src/srt/modules/community.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Unsupervised Community Discovery Head.
2
+
3
+ Discovers discourse communities from backbone hidden states without
4
+ predefined labels. A discourse community (in Peirce's framework) is a
5
+ group of language users who share interpretive norms — they assign similar
6
+ interpretants to the same representamens.
7
+
8
+ The community head runs at an early backbone layer (before MAH hooks) and
9
+ produces a soft assignment over K learned prototypes. The resulting community
10
+ vector conditions how MAH computes divergence, so the same sign can produce
11
+ different divergence patterns in different community contexts.
12
+
13
+ Training signal: the community prototypes are pulled apart by the semiotic
14
+ losses — if assigning text to different communities helps the model predict
15
+ divergence better, it will learn to separate them.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ from dataclasses import dataclass
21
+
22
+ import torch
23
+ import torch.nn as nn
24
+ import torch.nn.functional as F
25
+
26
+ from srt.config import CommunityConfig
27
+
28
+
29
+ @dataclass
30
+ class CommunityOutput:
31
+ """Output from community discovery.
32
+
33
+ When the head runs in continuous-trajectory mode (cfg.use_prototypes=False,
34
+ v8a), `logits` and `weights` are None and `vector == encoded`.
35
+ """
36
+
37
+ logits: torch.Tensor | None # (B, K) raw assignment scores, or None
38
+ weights: torch.Tensor | None # (B, K) soft assignment probabilities, or None
39
+ vector: torch.Tensor # (B, d_community) community embedding (mixture or encoded)
40
+ encoded: torch.Tensor # (B, d_community) pre-prototype-mixing encoder output
41
+
42
+
43
+ class CommunityDiscoveryHead(nn.Module):
44
+ """Soft clustering of hidden states into discourse communities.
45
+
46
+ With cfg.use_prototypes=True (default): pooled hidden state → encoder →
47
+ cosine similarity to K learned prototypes → soft assignment weights →
48
+ weighted mixture of prototypes as the community vector. This is the
49
+ v3–v7 architecture.
50
+
51
+ With cfg.use_prototypes=False (v8a): pooled hidden state → encoder →
52
+ the encoder output IS the community vector. No discrete basis. Motivated
53
+ by the v7 PCA finding that prototype tensors barely move from random
54
+ init; the encoder was already doing the discriminative work and the
55
+ soft-argmax over K anchors was throwing information away.
56
+ """
57
+
58
+ def __init__(self, cfg: CommunityConfig, d_backbone: int) -> None:
59
+ super().__init__()
60
+ self.temperature = cfg.temperature
61
+ self.use_prototypes = cfg.use_prototypes
62
+
63
+ # Encode pooled hidden states → community space
64
+ self.encoder = nn.Sequential(
65
+ nn.Linear(d_backbone, cfg.d_community),
66
+ nn.SiLU(),
67
+ )
68
+
69
+ # Learnable community prototypes (only when enabled)
70
+ if cfg.use_prototypes:
71
+ self.prototypes = nn.Embedding(cfg.num_prototypes, cfg.d_community)
72
+ else:
73
+ self.prototypes = None # type: ignore[assignment]
74
+
75
+ def forward(
76
+ self,
77
+ hidden_states: torch.Tensor,
78
+ attention_mask: torch.Tensor | None = None,
79
+ ) -> CommunityOutput:
80
+ """Discover community from hidden states.
81
+
82
+ Args:
83
+ hidden_states: (B, T, d_backbone) from an early backbone layer.
84
+ attention_mask: (B, T) padding mask (1 = real, 0 = pad). Optional.
85
+
86
+ Returns:
87
+ CommunityOutput. In prototype mode, logits/weights are populated
88
+ and vector is the prototype-weighted mixture. In trajectory mode
89
+ (use_prototypes=False), logits and weights are None and vector
90
+ equals encoded.
91
+ """
92
+ # Masked mean pool across positions → document-level representation
93
+ if attention_mask is not None:
94
+ mask = attention_mask.unsqueeze(-1).to(hidden_states.dtype) # (B, T, 1)
95
+ pooled = (hidden_states * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1)
96
+ else:
97
+ pooled = hidden_states.mean(dim=1) # (B, d_backbone)
98
+ encoded = self.encoder(pooled) # (B, d_community)
99
+
100
+ if not self.use_prototypes:
101
+ # v8a: continuous-trajectory mode — no discrete basis.
102
+ return CommunityOutput(
103
+ logits=None, weights=None, vector=encoded, encoded=encoded,
104
+ )
105
+
106
+ # Cosine similarity to prototypes
107
+ encoded_norm = F.normalize(encoded, dim=-1)
108
+ proto_norm = F.normalize(self.prototypes.weight, dim=-1)
109
+ logits = (encoded_norm @ proto_norm.T) / self.temperature # (B, K)
110
+
111
+ weights = F.softmax(logits, dim=-1) # (B, K)
112
+ vector = weights @ self.prototypes.weight # (B, d_community)
113
+
114
+ return CommunityOutput(
115
+ logits=logits, weights=weights, vector=vector, encoded=encoded,
116
+ )
src/srt/modules/mah.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Metapragmatic Attention Head (MAH).
2
+
3
+ Detects where meaning diverges across positions by computing the gap between
4
+ direct (local) interpretation and contextual (global) interpretation of each
5
+ token's hidden state. This is Peirce's "unlimited semiosis" made computational:
6
+ each sign (representamen) receives an interpretation (interpretant) that depends
7
+ on the surrounding discourse context. MAH quantifies where that context
8
+ *changes* the interpretation — i.e., where meaning forks.
9
+
10
+ The divergence vector d_t at position t captures:
11
+ d_t = f(interp_t) - g(attend(interp_{0..t}))
12
+ where f is direct projection, g is the contextual output after causal attention.
13
+ High ||d_t|| → the sign at position t means something different in context
14
+ than it would in isolation.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import math
20
+ from dataclasses import dataclass
21
+
22
+ import torch
23
+ import torch.nn as nn
24
+ import torch.nn.functional as F
25
+
26
+ from srt.config import MAHConfig
27
+
28
+
29
+ @dataclass
30
+ class MAHOutput:
31
+ """Output from a single MAH layer."""
32
+
33
+ divergence: torch.Tensor # (B, T, d_divergence)
34
+ attention_weights: torch.Tensor | None = None # (B, H, T, T)
35
+
36
+
37
+ class MetapragmaticAttentionHead(nn.Module):
38
+ """Single MAH layer that reads hidden states and produces divergence vectors."""
39
+
40
+ def __init__(self, cfg: MAHConfig, d_backbone: int, d_community: int = 0) -> None:
41
+ super().__init__()
42
+ d_sub = cfg.d_sub
43
+
44
+ # Project backbone hidden states → interpretant subspace
45
+ self.interp_proj = nn.Linear(d_backbone, d_sub, bias=False)
46
+
47
+ # Optional community conditioning
48
+ self.comm_proj: nn.Module | None = None
49
+ if d_community > 0:
50
+ self.comm_proj = nn.Linear(d_community, d_sub, bias=False)
51
+
52
+ # Multi-head self-attention in interpretant subspace
53
+ self.num_heads = cfg.num_heads
54
+ self.head_dim = d_sub // cfg.num_heads
55
+ assert d_sub % cfg.num_heads == 0
56
+
57
+ self.q_proj = nn.Linear(d_sub, d_sub, bias=False)
58
+ self.k_proj = nn.Linear(d_sub, d_sub, bias=False)
59
+ self.v_proj = nn.Linear(d_sub, d_sub, bias=False)
60
+ self.out_proj = nn.Linear(d_sub, d_sub, bias=False)
61
+ self.attn_dropout = nn.Dropout(cfg.dropout)
62
+
63
+ # Divergence output projection
64
+ self.div_proj = nn.Linear(d_sub, cfg.d_divergence, bias=False)
65
+
66
+ def forward(
67
+ self,
68
+ hidden_states: torch.Tensor,
69
+ community_vec: torch.Tensor | None = None,
70
+ causal_mask: torch.Tensor | None = None,
71
+ ) -> MAHOutput:
72
+ """Compute divergence from backbone hidden states.
73
+
74
+ Args:
75
+ hidden_states: (B, T, d_backbone) from a transformer layer.
76
+ community_vec: (B, d_community) soft community vector.
77
+ causal_mask: (1, 1, T, T) additive causal mask.
78
+
79
+ Returns:
80
+ MAHOutput with divergence vectors and optional attention weights.
81
+ """
82
+ B, T, _ = hidden_states.shape
83
+
84
+ # Project to interpretant subspace
85
+ interp = self.interp_proj(hidden_states) # (B, T, d_sub)
86
+
87
+ # Community conditioning: shift interpretant space
88
+ if community_vec is not None and self.comm_proj is not None:
89
+ comm_bias = self.comm_proj(community_vec) # (B, d_sub)
90
+ interp = interp + comm_bias.unsqueeze(1)
91
+
92
+ # Multi-head causal self-attention
93
+ q = self.q_proj(interp).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
94
+ k = self.k_proj(interp).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
95
+ v = self.v_proj(interp).view(B, T, self.num_heads, self.head_dim).transpose(1, 2)
96
+
97
+ attn = (q @ k.transpose(-2, -1)) / math.sqrt(self.head_dim)
98
+ if causal_mask is not None:
99
+ attn = attn + causal_mask
100
+ attn_weights = F.softmax(attn, dim=-1)
101
+ attn_weights = self.attn_dropout(attn_weights)
102
+
103
+ contextual = (attn_weights @ v).transpose(1, 2).reshape(B, T, -1)
104
+ contextual = self.out_proj(contextual) # (B, T, d_sub)
105
+
106
+ # Divergence = gap between direct and contextual interpretation
107
+ divergence = self.div_proj(interp - contextual) # (B, T, d_divergence)
108
+
109
+ return MAHOutput(divergence=divergence, attention_weights=attn_weights.detach())
src/srt/modules/rrm.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Reflexive Recurrent Module (RRM).
2
+
3
+ Tracks per-position semiotic meta-state via a GRU that processes divergence
4
+ observations from MAH layers. At injection points, produces a FiLM-style
5
+ modulation (gamma, beta) that multiplicatively + additively biases the
6
+ backbone's hidden states:
7
+
8
+ h' = h * (1 + gamma(meta_state)) + beta(meta_state)
9
+
10
+ The meta-state h_meta_t represents the model's accumulated awareness of
11
+ semiotic divergence at position t. Each MAH observation updates it:
12
+ h_meta_t^{l+1} = GRU(divergence_t^l, h_meta_t^l)
13
+
14
+ v3 used a single low-rank linear inject (gate * proj * scale) with
15
+ zero-initialized projection. Ablation showed the inject-back arm contributed
16
+ exactly nothing (every benchmark metric was identical to four decimal places
17
+ with injection forced to zero). The diagnosis was that the zero init plus
18
+ the inject-norm regularizer (which rewarded ||inj|| \u2248 1 regardless of
19
+ direction) drove the optimizer to satisfy the norm penalty with arbitrary
20
+ directions that were then orthogonal to the gradient signal from the frozen
21
+ backbone's CE.
22
+
23
+ v4 fixes both: FiLM modulation has a non-zero gradient pathway from the first
24
+ step (beta is initialized to zero so the forward is identity at init, but
25
+ gamma has small Gaussian init so dL/d(gamma_proj) flows immediately when the
26
+ downstream MAH layer's divergence is supervised by the bif/regime losses).
27
+ The inject-norm regularizer is dropped at the loss layer (LossConfig
28
+ inject_reg_weight = 0.0 by default in v4).
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ import torch
34
+ import torch.nn as nn
35
+
36
+ from srt.config import RRMConfig
37
+
38
+
39
+ class ReflexiveRecurrentModule(nn.Module):
40
+ """GRU-based reflexive meta-state tracker with FiLM-style injection."""
41
+
42
+ def __init__(self, cfg: RRMConfig, d_divergence: int, d_backbone: int) -> None:
43
+ super().__init__()
44
+ self.d_meta = cfg.d_meta
45
+ self.inject_scale = cfg.inject_scale
46
+ self.d_backbone = d_backbone
47
+
48
+ # Per-position GRU: processes divergence \u2192 meta-state
49
+ self.gru = nn.GRUCell(d_divergence, cfg.d_meta)
50
+
51
+ # FiLM projections: meta-state \u2192 (gamma, beta) in backbone-dim.
52
+ # gamma is multiplicative on (1 + gamma); beta is additive.
53
+ # gamma init: small Gaussian (std=0.02) so identity-at-init holds in
54
+ # expectation but gradient flows from the first step.
55
+ # beta init: zeros so identity-at-init is exact, then learns offsets.
56
+ self.gamma_proj = nn.Linear(cfg.d_meta, d_backbone, bias=True)
57
+ self.beta_proj = nn.Linear(cfg.d_meta, d_backbone, bias=True)
58
+ nn.init.normal_(self.gamma_proj.weight, std=0.02)
59
+ nn.init.zeros_(self.gamma_proj.bias)
60
+ nn.init.zeros_(self.beta_proj.weight)
61
+ nn.init.zeros_(self.beta_proj.bias)
62
+
63
+ def step(
64
+ self, divergence: torch.Tensor, meta_state: torch.Tensor | None
65
+ ) -> torch.Tensor:
66
+ """Update per-position meta-state with new divergence observation.
67
+
68
+ Args:
69
+ divergence: (B, T, d_divergence) from MAH.
70
+ meta_state: (B, T, d_meta) or None for initial state.
71
+
72
+ Returns:
73
+ Updated meta-state (B, T, d_meta).
74
+ """
75
+ B, T, d_div = divergence.shape
76
+ div_flat = divergence.reshape(B * T, d_div)
77
+
78
+ if meta_state is None:
79
+ meta_flat = torch.zeros(
80
+ B * T, self.d_meta, device=divergence.device, dtype=divergence.dtype
81
+ )
82
+ else:
83
+ meta_flat = meta_state.reshape(B * T, self.d_meta)
84
+
85
+ meta_flat = self.gru(div_flat, meta_flat)
86
+ return meta_flat.reshape(B, T, self.d_meta)
87
+
88
+ def inject(
89
+ self, meta_state: torch.Tensor, hidden_states: torch.Tensor
90
+ ) -> torch.Tensor:
91
+ """Produce FiLM modulation correction for backbone hidden states.
92
+
93
+ Returns the *correction* (h' - h) = h * gamma + beta, NOT h'. The
94
+ caller adds this to h to get h'. This keeps the rest of the adapter
95
+ and the diagnostic logging (injection norm tracking) unchanged: the
96
+ \"injection\" tensor is still the additive correction applied to h.
97
+
98
+ Args:
99
+ meta_state: (B, T, d_meta) current reflexive awareness.
100
+ hidden_states: (B, T, d_backbone) current hidden states.
101
+
102
+ Returns:
103
+ Correction vector (B, T, d_backbone) to add to hidden_states.
104
+ """
105
+ gamma = self.gamma_proj(meta_state) # (B, T, d_backbone)
106
+ beta = self.beta_proj(meta_state) # (B, T, d_backbone)
107
+ # FiLM: h' = h * (1 + gamma) + beta \u2192 correction = h * gamma + beta
108
+ correction = hidden_states * gamma + beta
109
+ return correction * self.inject_scale