instar 1.3.1183 → 1.3.1185

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
5
- "packageVersion": "1.3.1183",
5
+ "packageVersion": "1.3.1185",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "8105f34456934b67af19b7dffdb0b1bda68f1019301745cf73d6596155928f5a",
2
+ "sha256": "3eb1bd07851f19c2b7daa4f5bcf5b38a58e611750b8edfbe5f4836b7ee919418",
3
3
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
4
- "packageVersion": "1.3.1183"
4
+ "packageVersion": "1.3.1185"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
3
3
  "articleCount": 88,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1183"
5
+ "packageVersion": "1.3.1185"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.1183",
3
+ "version": "1.3.1185",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,6 +26,7 @@
26
26
  */
27
27
 
28
28
  import { execFileSync } from 'node:child_process';
29
+ import { readFileSync } from 'node:fs';
29
30
 
30
31
  // Keep in sync with inScope() in scripts/instar-dev-precommit.js — the gate
31
32
  // this check detects bypasses OF. (A drift here only weakens detection, never
@@ -40,18 +41,43 @@ export function isInScopeFile(file) {
40
41
  return false;
41
42
  }
42
43
 
43
- function isGateEvidence(change) {
44
- // Post-#827 per-entry file (added per gate evaluation)
45
- if (/^\.instar\/instar-dev-decisions\/.+\.json$/.test(change.file)) return true;
46
- // …or the pre-#827 legacy JSONL append (transition grace for in-flight PRs).
47
- if (change.file === '.instar/instar-dev-decisions.jsonl') return true;
48
- return false;
44
+ function isPerEntryDecisionFile(change) {
45
+ return /^\.instar\/instar-dev-decisions\/.+\.json$/.test(change.file);
46
+ }
47
+
48
+ function isLegacyDecisionFile(change) {
49
+ return change.file === '.instar/instar-dev-decisions.jsonl';
50
+ }
51
+
52
+ function recordScopeFiles(record) {
53
+ const files = record?.scope?.files;
54
+ return Array.isArray(files) ? files.filter((file) => typeof file === 'string') : [];
55
+ }
56
+
57
+ function coverageFiles(records) {
58
+ const covered = new Set();
59
+ for (const record of Array.isArray(records) ? records : []) {
60
+ for (const file of recordScopeFiles(record)) covered.add(file);
61
+ }
62
+ return covered;
63
+ }
64
+
65
+ function readDecisionAuditRecords(changes) {
66
+ const records = [];
67
+ for (const change of changes.filter(isPerEntryDecisionFile)) {
68
+ try {
69
+ records.push(JSON.parse(readFileSync(change.file, 'utf8')));
70
+ } catch {
71
+ records.push({});
72
+ }
73
+ }
74
+ return records;
49
75
  }
50
76
 
51
77
  /**
52
78
  * Pure evaluation — exported for unit tests.
53
- * @param {{ changes: Array<{status: string, file: string}>, title?: string, authorType?: string }} input
54
- * @returns {{ ok: boolean, exempt?: string, reason?: string, inScopeFiles?: string[] }}
79
+ * @param {{ changes: Array<{status: string, file: string}>, records?: unknown[], title?: string, authorType?: string }} input
80
+ * @returns {{ ok: boolean, exempt?: string, reason?: string, inScopeFiles?: string[], uncoveredFiles?: string[] }}
55
81
  */
56
82
  export function evaluateDecisionAuditPresence(input) {
57
83
  const title = String(input?.title ?? '');
@@ -64,21 +90,39 @@ export function evaluateDecisionAuditPresence(input) {
64
90
  return { ok: true, reason: 'no in-scope changes — local gate not required' };
65
91
  }
66
92
 
67
- const hasEvidence = changes.some(isGateEvidence);
68
- if (hasEvidence) {
69
- return { ok: true, reason: 'gate evidence present', inScopeFiles };
93
+ if (changes.some(isLegacyDecisionFile)) {
94
+ return { ok: true, reason: 'legacy decision-audit evidence present', inScopeFiles };
95
+ }
96
+
97
+ const recordChanges = changes.filter(isPerEntryDecisionFile);
98
+ if (recordChanges.length === 0) {
99
+ return {
100
+ ok: false,
101
+ inScopeFiles,
102
+ reason:
103
+ `This PR changes ${inScopeFiles.length} in-scope file(s) but carries NO decision-audit record — ` +
104
+ `the local instar-dev pre-commit gate did not run for these commits. The usual cause is a build ` +
105
+ `worktree without the husky shim (created with raw 'git worktree add' instead of 'instar worktree ` +
106
+ `create'). Fix: in the worktree run 'npm run prepare' (wires .husky/_), then re-commit so the gate ` +
107
+ `evaluates the change and its audit entry (.instar/instar-dev-decisions/<ts>-<slug>.json) rides the ` +
108
+ `commit. See tasks #81/#80 and PRs #827/#829.`,
109
+ };
110
+ }
111
+
112
+ const covered = coverageFiles(input?.records);
113
+ const uncoveredFiles = inScopeFiles.filter((file) => !covered.has(file));
114
+ if (uncoveredFiles.length === 0) {
115
+ return { ok: true, reason: 'decision-audit scope covers in-scope changes', inScopeFiles };
70
116
  }
71
117
 
72
118
  return {
73
119
  ok: false,
74
120
  inScopeFiles,
121
+ uncoveredFiles,
75
122
  reason:
76
- `This PR changes ${inScopeFiles.length} in-scope file(s) but carries NO decision-audit record ` +
77
- `the local instar-dev pre-commit gate did not run for these commits. The usual cause is a build ` +
78
- `worktree without the husky shim (created with raw 'git worktree add' instead of 'instar worktree ` +
79
- `create'). Fix: in the worktree run 'npm run prepare' (wires .husky/_), then re-commit so the gate ` +
80
- `evaluates the change and its audit entry (.instar/instar-dev-decisions/<ts>-<slug>.json) rides the ` +
81
- `commit. See tasks #81/#80 and PRs #827/#829.`,
123
+ `This PR changes ${inScopeFiles.length} in-scope file(s), but its decision-audit record scope ` +
124
+ `does not cover ${uncoveredFiles.length} in-scope file(s). Re-run the local instar-dev ` +
125
+ `pre-commit gate for this change so the decision-audit record declares the changed path(s).`,
82
126
  };
83
127
  }
84
128
 
@@ -114,8 +158,10 @@ if (invokedDirectly) {
114
158
  console.error(`decision-audit gate: git diff failed — ${err instanceof Error ? err.message : String(err)}`);
115
159
  process.exit(2);
116
160
  }
161
+ const changes = parseNameStatus(diffOut);
117
162
  const res = evaluateDecisionAuditPresence({
118
- changes: parseNameStatus(diffOut),
163
+ changes,
164
+ records: readDecisionAuditRecords(changes),
119
165
  title: process.env.PR_TITLE,
120
166
  authorType: process.env.PR_AUTHOR_TYPE,
121
167
  });
@@ -126,7 +172,7 @@ if (invokedDirectly) {
126
172
  console.error('decision-audit gate: FAIL');
127
173
  console.error(res.reason);
128
174
  console.error('');
129
- console.error('In-scope files without gate evidence:');
130
- for (const f of res.inScopeFiles ?? []) console.error(` - ${f}`);
175
+ console.error('In-scope files lacking decision-audit coverage:');
176
+ for (const f of res.uncoveredFiles ?? res.inScopeFiles ?? []) console.error(` - ${f}`);
131
177
  process.exit(1);
132
178
  }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-08-21T09:06:56.394Z",
5
- "instarVersion": "1.3.1183",
4
+ "generatedAt": "2026-08-21T16:44:51.648Z",
5
+ "instarVersion": "1.3.1185",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
5
- "packageVersion": "1.3.1183",
5
+ "packageVersion": "1.3.1185",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "8105f34456934b67af19b7dffdb0b1bda68f1019301745cf73d6596155928f5a",
2
+ "sha256": "3eb1bd07851f19c2b7daa4f5bcf5b38a58e611750b8edfbe5f4836b7ee919418",
3
3
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
4
- "packageVersion": "1.3.1183"
4
+ "packageVersion": "1.3.1185"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
3
3
  "articleCount": 88,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1183"
5
+ "packageVersion": "1.3.1185"
6
6
  }
@@ -5,6 +5,34 @@
5
5
 
6
6
  ## What Changed
7
7
 
8
+ The PR-boundary decision-audit gate now verifies that the audit record a pull request carries
9
+ actually **covers** the in-scope files that pull request changed, instead of only verifying that
10
+ some audit record was present.
11
+
12
+ Previously `evaluateDecisionAuditPresence()` asked one question — did any decision-audit record
13
+ change in this PR? — so an unrelated or stale-scoped record satisfied the gate while covering none
14
+ of the changed files. The gate reported "covered" while covering nothing.
15
+
16
+ The evaluator now accepts caller-supplied decision records, unions their readable `scope.files`, and
17
+ requires every in-scope changed path to appear in that union. The pure function still performs no
18
+ filesystem I/O; the CLI reads the per-entry JSON records and passes the parsed content in. A record
19
+ whose `scope.files` is absent, malformed, or unreadable contributes no coverage — the check fails
20
+ closed. Directory strings do not cover their descendants.
21
+
22
+ Unchanged: the bot-author, release-cut and no-in-scope-changes exemptions, and the legacy
23
+ `.instar/instar-dev-decisions.jsonl` transition allowance, which still short-circuits to a pass.
24
+
25
+ The failure output now names the specific uncovered files and the remedy (re-run the local gate so
26
+ the record declares those paths) rather than listing every in-scope file.
27
+
28
+ This is a CI-only gate script. There is no runtime surface, no route, no message, and no
29
+ agent-visible or user-visible behaviour change.
30
+
31
+ **Verdict is review-grade, not proven.** The five-property signature runner that would let a guard
32
+ be called fixed does not exist yet — `scratchpad/phaseB/B0.1-THE-BAR.md` is marked
33
+ `DRAFT-FOR-LANES` and its B0.2 implementation was never built — so nothing here is described as
34
+ fixing, verifying or proving the guard effective.
35
+
8
36
  A machine that receives an inbound Telegram message for a conversation it does **not** own no longer
9
37
  claims that message in its own exactly-once ingress ledger. It still relays the message to the owning
10
38
  machine exactly as before.
@@ -48,6 +76,18 @@ bookkeeping row is written.
48
76
 
49
77
  ## Evidence
50
78
 
79
+ - `npx vitest run tests/unit/decision-audit-presence-check.test.ts` — 14/14 passing, covering both
80
+ sides of the boundary: covering scope passes, stale scope fails, malformed scope fails closed,
81
+ directory strings do not cover descendants, multiple records union, and each existing exemption
82
+ still exempts.
83
+ - Side-effects review: `upgrades/side-effects/w22-decision-audit-scope-coverage.md`, including an
84
+ independent second-pass review that concurred and separately confirmed the diff introduces no new
85
+ schema, config, protocol surface or plumbing.
86
+ - Plain-English overview: `docs/specs/w22-decision-audit-scope-coverage.eli16.md`.
87
+ - The gap was identified by the Window-22 guard survey (`.instar/w22/branch-b-guard-ground-truth.md`),
88
+ which located this guard as one of four that prove less than they claim and rated this
89
+ identification the highest-confidence of the four.
90
+
51
91
  - Mechanism established from the code: `src/core/SessionRouter.ts` documents that an inbound for a
52
92
  topic owned by another live machine is forwarded to that machine over the mesh — so the non-owner
53
93
  is the relay, which is why the claim (and not the delivery) is what needed to change.
@@ -0,0 +1,37 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ A new manually-started maintenance job was added to this repository's own
9
+ pipeline: it marks ONE already-published version of the package deprecated,
10
+ with a plain-English pointer to the version people should use instead.
11
+
12
+ It exists because a release race can leave an ORPHAN on the registry — a
13
+ version that was published, but whose release commit and tag never landed in
14
+ history. Cleaning one up requires a publish-capable credential, and that
15
+ credential lives only inside this repository's pipeline. This job is the one
16
+ place it can be used for that purpose, and its capability is exactly one write.
17
+
18
+ The job is started by hand only. It never checks out the repository, so no
19
+ repository code runs beside the credential. It can deprecate; it cannot publish
20
+ and it cannot unpublish. The version and the message are supplied when it is
21
+ started, so nothing about the target is baked into the file. A range or a tag
22
+ is refused before the credential is touched, because those would deprecate many
23
+ versions in a single call, and the current recommended version is refused so
24
+ that what everyone installs can never be hit by a typo. Afterwards the result
25
+ is read back from the registry — the command exiting cleanly is not accepted as
26
+ proof on its own.
27
+
28
+ ## What to Tell Your User
29
+
30
+ Nothing. This is repository maintenance for the people who publish this
31
+ package; it changes no behaviour on any installed agent and adds no capability
32
+ an agent can reach.
33
+
34
+ ## Summary of New Capabilities
35
+
36
+ None for agents or users. One maintenance job for maintainers of this
37
+ repository.
@@ -0,0 +1,150 @@
1
+ # Side-Effects Review — decision-audit gate: scope coverage, not mere presence
2
+
3
+ **Change:** `scripts/decision-audit-presence-check.mjs` (+ its unit test)
4
+ **Tier:** 1 (declared). See "Tier declaration" at the bottom — the gate's advisory signal said 2.
5
+ **Branch:** `w22-b-audit-scope-coverage` · **Base:** `7d4076a53` (JKHeadley/main, v1.3.1182)
6
+
7
+ ## Summary of the change
8
+
9
+ The decision-audit presence gate ran on every PR and asked one question: did *some* decision-audit
10
+ record change in this PR? It never asked whether that record's declared scope covered the in-scope
11
+ files the PR actually touched. Any unrelated or stale-scoped record satisfied it. The gate reported
12
+ "covered" while covering nothing — a known false positive, and the exact shape the Window-22 guard
13
+ survey was chartered to find.
14
+
15
+ The evaluator now accepts caller-supplied decision records, unions their readable `scope.files`, and
16
+ requires every in-scope changed path to be covered by that union. The pure function does no
17
+ filesystem I/O; the CLI reads the per-entry JSON records and passes parsed content in.
18
+
19
+ ## Decision-point inventory
20
+
21
+ One decision point, already existing: pass/fail of the `decision-audit-gate` CI check on a PR. This
22
+ change does not create a decision point; it corrects the predicate of one that already blocks.
23
+
24
+ ## 1. Over-block
25
+
26
+ **What it now rejects that it did not before:** a PR that carries a decision-audit record whose
27
+ `scope.files` does not list every in-scope changed path. In practice this is a PR where the gate ran
28
+ for *some* of the commits but not the one that touched the extra file, or where files were added
29
+ after the last gate run.
30
+
31
+ This is the intended new rejection, but it is a genuine behaviour change for authors: previously the
32
+ presence of any record was enough. The failure message names the uncovered paths and tells the
33
+ author to re-run the local gate so the record declares them. Cost of a false block is "re-run the
34
+ gate and re-commit" — recoverable, local, no data loss.
35
+
36
+ **Deliberate non-tightening:** directory strings do *not* count as covering their descendants. That
37
+ choice makes the gate stricter than a permissive reading. It is recorded here because it is the one
38
+ place a reasonable reviewer might want the opposite; the writer emits concrete staged file paths, so
39
+ descendant-coverage would only matter for hand-written records.
40
+
41
+ ## 2. Under-block
42
+
43
+ Still missed, explicitly:
44
+
45
+ - A record whose `scope.files` lists the right paths but whose *content* is boilerplate. Coverage is
46
+ a structural claim, not a quality claim; nothing here reads the reasoning.
47
+ - A PR that changes an in-scope file, reverts it in a later commit, and carries a record scoped to
48
+ the intermediate state. The union is computed over declared paths, not over diff history.
49
+ - Files outside the in-scope predicate (`isInScopeFile`) are unaffected — this change does not widen
50
+ what counts as in-scope, and should not be read as doing so.
51
+ - Bot-authored and release-cut PRs remain exempt, unchanged.
52
+
53
+ ## 3. Level-of-abstraction fit
54
+
55
+ Correct layer. The check runs at the PR boundary, which is where the bypass it detects becomes
56
+ visible; the local pre-commit gate is the layer that *produces* the record, and it already does its
57
+ job. Pushing coverage down into the pre-commit writer would not help — the failure mode is commits
58
+ that never reached the writer at all.
59
+
60
+ No smarter gate exists that this should feed instead. The verdict is a set-membership fact, not a
61
+ judgment, so there is no authority for it to inform.
62
+
63
+ ## 4. Signal vs authority compliance
64
+
65
+ `docs/signal-vs-authority.md` applies to **judgment** decisions — blocking on what a message *means*
66
+ or what an agent's *intent* appears to be. This is not one. "Do the record's declared paths cover the
67
+ diff's paths?" is set membership over enumerable inputs, with no context required to separate the
68
+ legitimate case from the illegitimate one. It sits in the document's own excluded category:
69
+ structural validation at a boundary.
70
+
71
+ The change also moves the check in the *safe* direction relative to the principle: the check already
72
+ held blocking authority with a brittle predicate; this narrows a false-positive **pass**. It does not
73
+ add brittle authority, and it does not add a new blocker.
74
+
75
+ **Fails closed** on an absent, malformed, or unreadable `scope.files` — that record contributes no
76
+ coverage. Justified by the same document: a guard whose false-pass is cheap to exploit and whose
77
+ false-block costs a re-run should fail closed.
78
+
79
+ ## 5. Interactions
80
+
81
+ - **Legacy JSONL transition path** (`.instar/instar-dev-decisions.jsonl`) is checked *before* the
82
+ coverage logic and still short-circuits to pass. In-flight PRs on the old format are unaffected.
83
+ - **The "no record at all" branch** is preserved verbatim, including its long remediation message.
84
+ Only the new "record present but scope does not cover" branch is added.
85
+ - **The local pre-commit gate** is the producer of these records; nothing in this change alters what
86
+ it writes.
87
+ - No double-fire: the gate runs once per PR in one workflow.
88
+ - **Self-referential note:** this PR changes an in-scope file, so it must itself carry a record whose
89
+ scope covers `scripts/decision-audit-presence-check.mjs`. The change is therefore exercised against
90
+ itself at merge time.
91
+
92
+ ## 6. External surfaces
93
+
94
+ CI-visible only. The check's failure output changes (it now prints "In-scope files lacking
95
+ decision-audit coverage" and lists uncovered paths rather than all in-scope paths). No route, no
96
+ message, no agent-visible behaviour, no user-visible surface. No timing or runtime-state dependence.
97
+
98
+ ## 6b. Operator-surface quality
99
+
100
+ The failure message names the specific uncovered files and the concrete remedy (re-run the local
101
+ gate so the record declares the path). It does not require the reader to know the internals of the
102
+ record format. No operator action is required by this change itself.
103
+
104
+ ## 7. Multi-machine posture (Cross-Machine Coherence)
105
+
106
+ **Machine-local by design, and correctly so:** this runs in GitHub Actions against a PR, not on any
107
+ agent machine. There is no per-machine state, no replication path, and nothing to strand on a topic
108
+ transfer. The decision records it reads are repository content, identical on every checkout.
109
+
110
+ ## 8. Rollback cost
111
+
112
+ Single-file revert of `scripts/decision-audit-presence-check.mjs` (plus its test). No migration, no
113
+ persisted state, no agent repair. A PR blocked by the new predicate is unblocked by reverting or by
114
+ adding a covering record. Worst realistic case: a burst of PRs fail the gate until authors re-run
115
+ the local gate — visible immediately, recoverable in minutes.
116
+
117
+ ## Conclusion
118
+
119
+ Bounded correction of an existing gate's predicate, in the direction of fewer false passes, with a
120
+ recoverable false-block cost. No new decision point, no new authority, no runtime surface.
121
+
122
+ **Verdict is REVIEW-GRADE, not proven.** The five-property signature test that would let a guard be
123
+ called *fixed* does not exist — `scratchpad/phaseB/B0.1-THE-BAR.md` is marked `DRAFT-FOR-LANES` and
124
+ its B0.2 runner was never built. Nothing in this change may be described as fixing, verifying, or
125
+ proving the guard is effective.
126
+
127
+ ## Tier declaration (recorded because the signal disagreed)
128
+
129
+ The gate's advisory printed `suggestedTier=2 (size=2, riskFloor=1, 86 LOC across 1 file(s))` — the
130
+ **risk** floor is 1; **size** raised the suggestion to 2, and the size is mostly added tests.
131
+ Observer-1 ruling ten (2026-08-21, topic 29723) declared this change tier 1, reasoning on the record:
132
+ the higher tier exists to force design convergence before code, and that convergence exists for this
133
+ change in the Window-22 causal map, the charter, and rulings eight through ten — mechanism proven,
134
+ predicted after-state stated, falsification condition stated. The ruling is void if the diff carries
135
+ new plumbing, a schema, config, or a protocol surface; it does not (one script, one test file).
136
+
137
+ This paragraph exists so the disagreement between the signal and the declaration is auditable rather
138
+ than suppressed.
139
+
140
+ ## Evidence pointers
141
+
142
+ - 14/14 targeted tests pass: `npx vitest run tests/unit/decision-audit-presence-check.test.ts`
143
+ - Guard survey that identified the gap: `.instar/w22/branch-b-guard-ground-truth.md`
144
+ - Findings ledger entry 36: view `6e25dfa2-f374-409b-9e85-57aa210adfe0`
145
+
146
+ ## Second-pass review
147
+
148
+ Concur with the review. I checked the artifact, docs/signal-vs-authority.md, and the actual git diff for the worktree: the diff is limited to scripts/decision-audit-presence-check.mjs and its unit test, and it changes the existing PR gate from per-entry record presence to exact in-scope-path coverage by the union of parsed scope.files. The over-block section is honest: absent, malformed, unreadable, different-file, and directory-only scopes now contribute no coverage and can reject the PR, while legacy JSONL still passes before coverage logic. The signal-vs-authority argument is sound because this is structural boundary validation over enumerable changed paths and declared record paths, not a judgment about message meaning or intent. I found no unmentioned behavioral surface in the diff and no new schema, config, protocol surface, or tier-voiding plumbing beyond local parsing/passing of the already-existing decision-record content.
149
+
150
+ Independent reviewer, 2026-08-21T01:10:23-07:00