instar 1.3.349 → 1.3.350

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.
@@ -0,0 +1,64 @@
1
+ /**
2
+ * PrincipalGuard — cross-principal attribution detector (EXO 3.0 / "Know Your
3
+ * Principal" standard, Phase 1 brain).
4
+ *
5
+ * The "Caroline" incident (2026-06-05, topic 19437): an autonomous session
6
+ * silently adopted a real OTHER principal as its operator and credited the
7
+ * actual operator's decisions to her across three docs — and no gate watched,
8
+ * because the contamination was in the agent's OWN output, not an inbound
9
+ * message. This module is the pure-logic detector for that failure: given a
10
+ * piece of agent-authored text, it finds operator-ROLE-decision attributions
11
+ * ("X approved", "locked with X", "mandate (X)", "X dropped a token") and
12
+ * flags any whose attributed principal is NOT the topic's bound operator and
13
+ * does NOT resolve to a known user.
14
+ *
15
+ * Pure + deterministic so it is unit-testable in isolation; it performs no I/O
16
+ * and is not wired into the live request path here (that is a later increment).
17
+ * The constitution standard it enforces: "Know Your Principal — An Unverified
18
+ * Identity Is a Guess" (docs/STANDARDS-REGISTRY.md). Spec:
19
+ * docs/specs/OPERATOR-IDENTITY-BINDING-SPEC.md.
20
+ */
21
+ /** A verified operator, established ONLY from an authenticated channel — never
22
+ * from a name read in content (that is the whole point). */
23
+ export interface VerifiedOperator {
24
+ /** The platform-verified sender id (e.g. Telegram uid). */
25
+ uid: string;
26
+ /** Display name(s) the operator is known by, lowercased for matching. */
27
+ names: string[];
28
+ }
29
+ /**
30
+ * Establish a topic's operator from the AUTHENTICATED sender — never from a
31
+ * content name. The uid is the authority; names are only for matching the
32
+ * agent's prose against the verified principal. A blank uid yields no operator
33
+ * (an unbound topic), which the guard treats as "everything is unverified".
34
+ */
35
+ export declare function establishOperator(authenticatedUid: string, displayName?: string): VerifiedOperator | null;
36
+ /** The kind of operator-role decision an attribution carries — drives warn vs block. */
37
+ export type AttributionKind = 'approval' | 'mandate' | 'credential' | 'lock' | 'acting-for';
38
+ export interface Attribution {
39
+ /** The principal name the decision was attributed to (lowercased). */
40
+ principal: string;
41
+ kind: AttributionKind;
42
+ /** The matched snippet, for the audit trail. */
43
+ snippet: string;
44
+ }
45
+ /** Find every operator-role-decision attribution in agent-authored text. */
46
+ export declare function detectAttributions(text: string): Attribution[];
47
+ export type Verdict = 'ok' | 'warn' | 'block';
48
+ export interface PrincipalFinding {
49
+ attribution: Attribution;
50
+ verdict: Verdict;
51
+ reason: string;
52
+ }
53
+ /**
54
+ * Evaluate a piece of agent-authored text against the topic's verified
55
+ * operator and the known-user registry. An attribution to the bound operator
56
+ * (or any known user) is fine; an attribution to anyone else is the Caroline
57
+ * failure — warn for prose, block for authority/credential decisions.
58
+ *
59
+ * `knownUserNames` are lowercased display names from UserManager (the
60
+ * authoritative registry). Resolution against it is what the "Know Your
61
+ * Principal" standard requires before any principal is accepted.
62
+ */
63
+ export declare function evaluatePrincipalCoherence(text: string, operator: VerifiedOperator | null, knownUserNames?: string[]): PrincipalFinding[];
64
+ //# sourceMappingURL=PrincipalGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrincipalGuard.d.ts","sourceRoot":"","sources":["../../src/core/PrincipalGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;6DAC6D;AAC7D,MAAM,WAAW,gBAAgB;IAC/B,2DAA2D;IAC3D,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAKzG;AAED,wFAAwF;AACxF,MAAM,MAAM,eAAe,GACvB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,MAAM,GACN,YAAY,CAAC;AAEjB,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,eAAe,CAAC;IACtB,gDAAgD;IAChD,OAAO,EAAE,MAAM,CAAC;CACjB;AA0CD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAgB9D;AAED,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,gBAAgB,GAAG,IAAI,EACjC,cAAc,GAAE,MAAM,EAAO,GAC5B,gBAAgB,EAAE,CAqBpB"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * PrincipalGuard — cross-principal attribution detector (EXO 3.0 / "Know Your
3
+ * Principal" standard, Phase 1 brain).
4
+ *
5
+ * The "Caroline" incident (2026-06-05, topic 19437): an autonomous session
6
+ * silently adopted a real OTHER principal as its operator and credited the
7
+ * actual operator's decisions to her across three docs — and no gate watched,
8
+ * because the contamination was in the agent's OWN output, not an inbound
9
+ * message. This module is the pure-logic detector for that failure: given a
10
+ * piece of agent-authored text, it finds operator-ROLE-decision attributions
11
+ * ("X approved", "locked with X", "mandate (X)", "X dropped a token") and
12
+ * flags any whose attributed principal is NOT the topic's bound operator and
13
+ * does NOT resolve to a known user.
14
+ *
15
+ * Pure + deterministic so it is unit-testable in isolation; it performs no I/O
16
+ * and is not wired into the live request path here (that is a later increment).
17
+ * The constitution standard it enforces: "Know Your Principal — An Unverified
18
+ * Identity Is a Guess" (docs/STANDARDS-REGISTRY.md). Spec:
19
+ * docs/specs/OPERATOR-IDENTITY-BINDING-SPEC.md.
20
+ */
21
+ /**
22
+ * Establish a topic's operator from the AUTHENTICATED sender — never from a
23
+ * content name. The uid is the authority; names are only for matching the
24
+ * agent's prose against the verified principal. A blank uid yields no operator
25
+ * (an unbound topic), which the guard treats as "everything is unverified".
26
+ */
27
+ export function establishOperator(authenticatedUid, displayName) {
28
+ const uid = String(authenticatedUid ?? '').trim();
29
+ if (!uid)
30
+ return null;
31
+ const names = (displayName ?? '').trim() ? [displayName.trim().toLowerCase()] : [];
32
+ return { uid, names };
33
+ }
34
+ // ── Detection patterns ───────────────────────────────────────────────
35
+ // Each captures a person-like principal NAME in group 1. Kept conservative:
36
+ // a name is 1-3 capitalized-ish words (so "the team" / "prod" don't match).
37
+ // NAME requires a real capital first letter (case-SENSITIVE — no `i` flag — so
38
+ // "have"/"production" can never be captured as a principal). Only the keyword
39
+ // LEADS are made case-flexible ([Mm]andate, [Ll]ock…) so a sentence-initial
40
+ // "Mandate" matches as well as a mid-sentence "mandate".
41
+ const NAME = String.raw `([A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+){0,2})`;
42
+ // Bounded gap that lets a date/short clause sit between a keyword and the name
43
+ // ("Locked 2026-06-04 with Caroline") without swallowing a sentence.
44
+ const GAP = String.raw `[^.\n]{0,40}?`;
45
+ const PATTERNS = [
46
+ { re: new RegExp(String.raw `\b[Mm]andate\s*\(\s*${NAME}\s*\)`, 'g'), kind: 'mandate' },
47
+ { re: new RegExp(String.raw `\b${NAME}\s+[Aa]uthoriz(?:ed|es)\b`, 'g'), kind: 'mandate' },
48
+ { re: new RegExp(String.raw `\b${NAME}\s+[Aa]pprov(?:ed|es)\b`, 'g'), kind: 'approval' },
49
+ { re: new RegExp(String.raw `\b(?:[Bb]lessed|[Ss]igned[- ]off)\s+by\s+${NAME}`, 'g'), kind: 'approval' },
50
+ { re: new RegExp(String.raw `\b[Ll]ock(?:ed)?\b${GAP}\b(?:with|by)\s+${NAME}`, 'g'), kind: 'lock' },
51
+ { re: new RegExp(String.raw `\b[Ss]tanding requirement from\s+${NAME}`, 'g'), kind: 'lock' },
52
+ { re: new RegExp(String.raw `\b${NAME}\s+(?:dropped|drop)\s+(?:a|the)\s+(?:token|credential|secret|key)`, 'g'), kind: 'credential' },
53
+ { re: new RegExp(String.raw `\b${NAME}['’]s\s+(?:credential|token|git\s+cred)`, 'g'), kind: 'credential' },
54
+ { re: new RegExp(String.raw `\b[Oo]n\s+behalf\s+of\s+${NAME}`, 'g'), kind: 'acting-for' },
55
+ { re: new RegExp(String.raw `\b[Oo]n\s+${NAME}['’]s\s+(?:say-so|behalf|authority)`, 'g'), kind: 'acting-for' },
56
+ ];
57
+ // Words that look capitalized but are never principals (sentence-start nouns,
58
+ // product/role terms). Keeps the detector from flagging "Production approved".
59
+ const NON_PRINCIPALS = new Set([
60
+ 'the', 'a', 'an', 'prod', 'production', 'ci', 'team', 'board', 'ops', 'staging',
61
+ 'github', 'telegram', 'slack', 'dawn', 'echo', 'i', 'we', 'you', 'they', 'it',
62
+ 'have', 'has', 'this', 'that', 'every', 'all',
63
+ ]);
64
+ function isPersonLike(name) {
65
+ const firstWord = name.split(/\s+/)[0];
66
+ // The `i` flag lets [A-Z] match lowercase too, so require an actual capital
67
+ // first letter here — a real person name is capitalized; "production" is not.
68
+ const startsCapital = /^[A-Z]/.test(firstWord);
69
+ return startsCapital && !NON_PRINCIPALS.has(firstWord.toLowerCase());
70
+ }
71
+ /** Find every operator-role-decision attribution in agent-authored text. */
72
+ export function detectAttributions(text) {
73
+ const out = [];
74
+ const seen = new Set();
75
+ for (const { re, kind } of PATTERNS) {
76
+ re.lastIndex = 0;
77
+ let m;
78
+ while ((m = re.exec(text)) !== null) {
79
+ const principal = (m[1] ?? '').trim();
80
+ if (!principal || !isPersonLike(principal))
81
+ continue;
82
+ const key = `${kind}:${principal.toLowerCase()}:${m.index}`;
83
+ if (seen.has(key))
84
+ continue;
85
+ seen.add(key);
86
+ out.push({ principal: principal.toLowerCase(), kind, snippet: m[0] });
87
+ }
88
+ }
89
+ return out;
90
+ }
91
+ /** Credential/authority-bearing kinds BLOCK when misattributed; the rest WARN. */
92
+ const BLOCK_KINDS = new Set(['mandate', 'credential']);
93
+ /**
94
+ * Evaluate a piece of agent-authored text against the topic's verified
95
+ * operator and the known-user registry. An attribution to the bound operator
96
+ * (or any known user) is fine; an attribution to anyone else is the Caroline
97
+ * failure — warn for prose, block for authority/credential decisions.
98
+ *
99
+ * `knownUserNames` are lowercased display names from UserManager (the
100
+ * authoritative registry). Resolution against it is what the "Know Your
101
+ * Principal" standard requires before any principal is accepted.
102
+ */
103
+ export function evaluatePrincipalCoherence(text, operator, knownUserNames = []) {
104
+ const known = new Set([
105
+ ...(operator?.names ?? []),
106
+ ...knownUserNames.map((n) => n.trim().toLowerCase()).filter(Boolean),
107
+ ]);
108
+ const findings = [];
109
+ for (const attribution of detectAttributions(text)) {
110
+ if (known.has(attribution.principal))
111
+ continue; // resolves to a verified principal — fine
112
+ const verdict = BLOCK_KINDS.has(attribution.kind) ? 'block' : 'warn';
113
+ findings.push({
114
+ attribution,
115
+ verdict,
116
+ reason: `Operator-role ${attribution.kind} attributed to "${attribution.principal}", who is ` +
117
+ (operator
118
+ ? `not this topic's bound operator and does not resolve to a known user`
119
+ : `unverifiable — this topic has no bound operator`) +
120
+ `. Know Your Principal: an unrecognized party in a decision role is a question to resolve, not a fact to accept.`,
121
+ });
122
+ }
123
+ return findings;
124
+ }
125
+ //# sourceMappingURL=PrincipalGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrincipalGuard.js","sourceRoot":"","sources":["../../src/core/PrincipalGuard.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.349",
3
+ "version": "1.3.350",
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",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-06-06T06:49:15.721Z",
5
- "instarVersion": "1.3.349",
4
+ "generatedAt": "2026-06-06T06:52:19.246Z",
5
+ "instarVersion": "1.3.350",
6
6
  "entryCount": 199,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -5,6 +5,15 @@
5
5
 
6
6
  ## What Changed
7
7
 
8
+ Added `src/core/PrincipalGuard.ts` — the pure-logic brain of the "Know Your
9
+ Principal" cross-principal coherence guard (constitution standard ratified in
10
+ #898). It establishes a topic's operator only from the authenticated sender,
11
+ detects operator-role-decision attributions in agent-authored text ("X
12
+ approved", "Mandate (X)", "locked with X", "X dropped a token"), and flags any
13
+ credited to a principal who is neither the bound operator nor a known user —
14
+ blocking authority/credential misattributions, warning on prose. No runtime
15
+ consumers yet (the wiring is a later increment).
16
+
8
17
  Fixes #866: `POST /projects/:id/advance` building→merged always failed
9
18
  `GH_PR_VIEW_UNAVAILABLE` because `StageTransitionValidator` has no internal
10
19
  default for `ghPrView`/`gitMergeBaseIsAncestor` and the route never injected
@@ -16,6 +25,10 @@ error can never recur.
16
25
 
17
26
  ## What to Tell Your User
18
27
 
28
+ Nothing user-facing changes. This is foundation code (experimental) for the
29
+ security fix behind the Caroline identity-bleed incident — it does not alter any
30
+ current behavior.
31
+
19
32
  If you drive a multi-spec initiative through the projects pipeline, its
20
33
  items can now actually reach the "merged" stage — the system verifies the
21
34
  work's pull request really merged (merge state, commit reachable on the
@@ -24,6 +37,9 @@ finally tracks reality through the whole pipeline.
24
37
 
25
38
  ## Summary of New Capabilities
26
39
 
40
+ - `PrincipalGuard` (experimental, no runtime consumers yet): `establishOperator`,
41
+ `detectAttributions`, `evaluatePrincipalCoherence`.
42
+
27
43
  - `POST /projects/:id/advance` with `targetStage: "merged"` now works end to
28
44
  end (was structurally impossible): it verifies the PR via `gh pr view` and
29
45
  confirms the merge commit is reachable on `origin/main` before recording
@@ -31,6 +47,11 @@ finally tracks reality through the whole pipeline.
31
47
 
32
48
  ## Evidence
33
49
 
50
+ Net-new capability. Verified by 13 unit tests including the incident-replay
51
+ regression test (the three real Caroline doc lines all caught with the topic
52
+ bound to the operator; the same lines crediting the operator all pass) and a
53
+ clean `tsc --noEmit`.
54
+
34
55
  - tests/integration/projects-api.test.ts: building→merged now returns
35
56
  GH_PR_VIEW_FAILED (not UNAVAILABLE), deterministic regardless of whether
36
57
  gh is installed/authed. Full projects-api (45) + StageTransitionValidator
@@ -0,0 +1,39 @@
1
+ # Side-effects review — PrincipalGuard (Phase 1 brain)
2
+
3
+ ## What this change is
4
+ A new pure-logic module `src/core/PrincipalGuard.ts` (operator establishment +
5
+ cross-principal attribution detector + warn/block evaluator) and its unit test.
6
+ Implements the detector half of the just-ratified "Know Your Principal" standard
7
+ (the cross-principal coherence guard from OPERATOR-IDENTITY-BINDING-SPEC.md §2).
8
+
9
+ ## Blast radius
10
+ - **Runtime impact: none.** Nothing imports `PrincipalGuard` at boot or in any
11
+ route/job/sentinel yet. Adding the file cannot change live behavior. It is the
12
+ testable brain; wiring it into the topic-operator binding, session-start
13
+ injection, and the outbound/at-rest review path are explicit later increments.
14
+ - **No I/O, no network, no new config/route/migration surface.** Pure functions
15
+ over strings + a `VerifiedOperator` value object.
16
+ - **Establishment is uid-only by construction:** `establishOperator` takes the
17
+ authenticated sender uid; there is NO code path that accepts a name from
18
+ content as the operator (the Caroline failure mode is impossible by type).
19
+
20
+ ## Security review
21
+ - The detector is conservative: a name must be capitalized (case-sensitive,
22
+ not the `i` flag) and not in a non-principal stop-set, so "Production
23
+ approved" / "The Board approved" do not flag. False positives warn (prose) or
24
+ block (authority/credential) — they never silently pass a misattribution.
25
+ - No payload/secret handling; operates only on already-authored text.
26
+
27
+ ## Framework generality
28
+ Pure logic, no session-launch/inject/message-delivery surface — framework-agnostic.
29
+
30
+ ## Test coverage
31
+ 13 unit tests covering both sides of every boundary: uid-only establishment
32
+ (incl. blank → null), each detection shape, non-principal rejection,
33
+ bound-operator pass, known-user pass, unknown-principal flag with block-vs-warn
34
+ tiering, no-operator case, and the **incident-replay regression test** — the
35
+ three real Caroline doc lines all caught (block for mandate/credential), and the
36
+ same lines crediting the bound operator all pass. `tsc --noEmit` clean.
37
+
38
+ ## Rollback
39
+ Delete the module + test; zero runtime consequence (no consumers).