instar 1.3.946 → 1.3.948

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,11 @@
1
+ /** Deterministic UX assertions for Tier-3 user-surface tests. */
2
+ /** Reject internal identifiers, paths, config keys, and known implementation jargon. */
3
+ export declare function assertPlainEnglish(text: unknown): asserts text is string;
4
+ /** Every failure must explain what happened and give the user a next action. */
5
+ export declare function assertHonestFailure(state: unknown): asserts state is {
6
+ message: string;
7
+ actionable: boolean;
8
+ };
9
+ /** A queue must not contain an expired/stale entry that can be redelivered. */
10
+ export declare function assertNoZombie(queue: unknown): asserts queue is Array<Record<string, unknown>>;
11
+ //# sourceMappingURL=assertUserVisible.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertUserVisible.d.ts","sourceRoot":"","sources":["../../../src/messaging/detectors/assertUserVisible.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAWjE,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAKxE;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAO7G;AAED,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAa9F"}
@@ -0,0 +1,47 @@
1
+ /** Deterministic UX assertions for Tier-3 user-surface tests. */
2
+ const INTERNAL_ID = /\b(?:CMT|ACT|PR)[-_#]?\d+\b/i;
3
+ const FILE_PATH = /(?:^|\s)(?:\/|\.\.\/|src\/|tests\/)[^\s]+/;
4
+ const CONFIG_KEY = /\b[a-z][a-z0-9]*(?:\.[a-z][a-z0-9]*){2,}\b/;
5
+ const JARGON_CLASSES = [/\b(?:hydration|idempotency|backpressure|fan[- ]?out|reconciliation)\b/i];
6
+ function requireText(text, label) {
7
+ if (typeof text !== 'string' || text.trim().length === 0)
8
+ throw new Error(`${label} must be plain user-visible text`);
9
+ }
10
+ /** Reject internal identifiers, paths, config keys, and known implementation jargon. */
11
+ export function assertPlainEnglish(text) {
12
+ requireText(text, 'text');
13
+ if (INTERNAL_ID.test(text) || FILE_PATH.test(text) || CONFIG_KEY.test(text) || JARGON_CLASSES.some((p) => p.test(text))) {
14
+ throw new Error('text contains implementation detail or jargon');
15
+ }
16
+ }
17
+ /** Every failure must explain what happened and give the user a next action. */
18
+ export function assertHonestFailure(state) {
19
+ if (!state || typeof state !== 'object')
20
+ throw new Error('failure state is missing');
21
+ const candidate = state;
22
+ requireText(candidate.message, 'failure message');
23
+ if (candidate.ok === true)
24
+ throw new Error('success state cannot be asserted as a failure');
25
+ if (candidate.actionable !== true)
26
+ throw new Error('failure message must include an actionable next step');
27
+ assertPlainEnglish(candidate.message);
28
+ }
29
+ /** A queue must not contain an expired/stale entry that can be redelivered. */
30
+ export function assertNoZombie(queue) {
31
+ if (!Array.isArray(queue))
32
+ throw new Error('queue must be an array');
33
+ for (const item of queue) {
34
+ if (!item || typeof item !== 'object')
35
+ throw new Error('queue contains an invalid entry');
36
+ const row = item;
37
+ if (row.status === 'queued' && row.expiresAt && Date.parse(String(row.expiresAt)) <= Date.now()) {
38
+ throw new Error('queue contains an expired queued entry');
39
+ }
40
+ if (row.status === 'queued' && row.nextAttemptAt && Date.parse(String(row.nextAttemptAt)) > Date.now())
41
+ continue;
42
+ if (row.status === 'queued' && row.createdAt && Date.now() - Date.parse(String(row.createdAt)) > 24 * 60 * 60 * 1000) {
43
+ throw new Error('queue contains a stale queued entry');
44
+ }
45
+ }
46
+ }
47
+ //# sourceMappingURL=assertUserVisible.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertUserVisible.js","sourceRoot":"","sources":["../../../src/messaging/detectors/assertUserVisible.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.946",
3
+ "version": "1.3.948",
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",
@@ -29,7 +29,11 @@ export const MIN_ELI16_CHARS = 800;
29
29
  export function resolveEli16Path(specPath, specFm) {
30
30
  const specDir = path.dirname(specPath);
31
31
  const specBase = path.basename(specPath, '.md');
32
- const siblingPath = path.join(specDir, `${specBase}.eli16.md`);
32
+ // Specs already named *.eli16.md use a readable overview sibling; never
33
+ // manufacture the historical *.eli16.eli16.md suffix.
34
+ const siblingPath = specBase.endsWith('.eli16')
35
+ ? path.join(specDir, `${specBase.slice(0, -'.eli16'.length)}.overview.md`)
36
+ : path.join(specDir, `${specBase}.eli16.md`);
33
37
  const fmMatch = specFm.match(/^\s*eli16-overview\s*:\s*["']?([^"'\n]+)/m);
34
38
  if (fmMatch) {
35
39
  const declared = fmMatch[1].trim().replace(/["']/g, '');
@@ -77,6 +77,7 @@ const branchRuleset = {
77
77
  strict_required_status_checks_policy: true,
78
78
  required_status_checks: [
79
79
  { context: 'worktree-trailer-sig-check / verify' },
80
+ { context: 'UX impact declaration' },
80
81
  ],
81
82
  },
82
83
  },
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ // safe-git-allow: read-only base-ref diff inspection for pull-request lint
3
+ /** L1 UX-impact declaration lint. Exit 0=pass/out-of-scope, 1=violation, 2=internal error. */
4
+ import { execFileSync } from 'node:child_process';
5
+ import { writeFileSync } from 'node:fs';
6
+
7
+ function arg(name) { const i = process.argv.indexOf(name); return i >= 0 ? process.argv[i + 1] : ''; }
8
+ const base = arg('--base');
9
+ const head = arg('--head') || 'HEAD';
10
+ const body = arg('--body') || '';
11
+ const scope = (arg('--scope') || '').split(',').map((v) => v.trim().toLowerCase()).filter(Boolean);
12
+ const pusher = (arg('--pusher') || '').trim().toLowerCase();
13
+ const reportPath = arg('--report');
14
+ if (process.env.INSTAR_UX_LINT === 'off') { console.log('UX lint disabled by literal kill switch'); process.exit(0); }
15
+ if (!base) { console.error('::error::UX lint requires a base ref'); process.exit(2); }
16
+ try {
17
+ const names = execFileSync('git', ['diff', '--name-only', `${base}...${head}`], { encoding: 'utf8' }).trim().split('\n').filter(Boolean);
18
+ const commits = execFileSync('git', ['log', '--format=%an <%ae>%n%cn <%ce>', `${base}..${head}`], { encoding: 'utf8' }).toLowerCase();
19
+ const authorInScope = scope.length === 0
20
+ || (Boolean(pusher) && scope.includes(pusher))
21
+ || scope.some((token) => commits.includes(token));
22
+ const report = { version: 1, base, head, authorInScope, scope, allowlistedPaths: [], exempt: false, internalError: false };
23
+ const writeReport = () => { if (reportPath) writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); };
24
+ if (!authorInScope) { report.outOfScopeAuthor = true; await writeReport(); console.log('UX lint: out-of-scope author'); process.exit(0); }
25
+ const allowlisted = names.filter((p) => p === 'src/server/routes.ts' || p === 'src/commands/server.ts' || p.startsWith('src/messaging/') || p.startsWith('src/dashboard/') || p.startsWith('src/templates/'));
26
+ report.allowlistedPaths = allowlisted;
27
+ if (allowlisted.length === 0) { await writeReport(); console.log('UX lint: out of scope'); process.exit(0); }
28
+ const diff = execFileSync('git', ['diff', '--unified=0', `${base}...${head}`, '--', ...allowlisted], { encoding: 'utf8' });
29
+ const added = diff.split('\n').filter((line) => line.startsWith('+') && !line.startsWith('+++')).join('\n');
30
+ const refactorOnly = !allowlisted.some((p) => p.startsWith('src/templates/') || p === 'src/server/routes.ts' || p === 'src/commands/server.ts')
31
+ && !/(?:^|\s)[`'\"](?:[^`'\"]+)[`'\"]/.test(added);
32
+ const section = body.match(/^## UX Impact\s*\n([\s\S]*?)(?=^##\s|(?![\s\S]))/im)?.[1]?.trim() || '';
33
+ if (/UX-Impact:\s*refactor-only/i.test(section) && refactorOnly) { report.exempt = true; report.exemption = 'refactor-only'; await writeReport(); console.log('UX lint PASS: deterministic refactor-only exemption'); process.exit(0); }
34
+ if (!section) { console.error('::error::UX Impact section is required for user-facing paths'); process.exit(1); }
35
+ if (/UX-Impact:\s*none/i.test(section)) { console.error('::error::UX-Impact: none is not allowed for allowlisted paths'); process.exit(1); }
36
+ if (!/who\s+sees|what\s+(?:the\s+)?user|first[- ]contact|user[- ]visible/i.test(section)) {
37
+ console.error('::error::UX Impact must describe audience, visible behavior, and first contact'); process.exit(1);
38
+ }
39
+ const quoted = [...section.matchAll(/[`'"“]([^`'"”]+)[`'"”]/g)].map((m) => m[1]);
40
+ if (!quoted.some((q) => q.length > 2 && diff.includes(q))) {
41
+ console.error('::error::UX Impact must quote a concrete string from the diff'); process.exit(1);
42
+ }
43
+ await writeReport();
44
+ console.log(`UX lint PASS: ${allowlisted.length} allowlisted path(s)`);
45
+ process.exit(0);
46
+ } catch (error) {
47
+ if (reportPath) writeFileSync(reportPath, `${JSON.stringify({ version: 1, internalError: true, message: error instanceof Error ? error.message : String(error) })}\n`);
48
+ console.error(`::error::UX lint internal error: ${error instanceof Error ? error.message : String(error)}`);
49
+ process.exit(2);
50
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-07-24T23:49:53.588Z",
5
- "instarVersion": "1.3.946",
4
+ "generatedAt": "2026-07-25T01:11:00.882Z",
5
+ "instarVersion": "1.3.948",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -0,0 +1,21 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ Pull requests touching messaging and other user-facing surfaces now receive a
9
+ separate UX-impact declaration check. The messaging E2E suite also exercises
10
+ plain-language, honest-failure, and zombie-queue assertions.
11
+
12
+ ## What to Tell Your User
13
+
14
+ Changes that affect people must explain the visible experience in the PR, and
15
+ CI now checks that explanation alongside a real messaging scenario.
16
+
17
+ ## Summary of New Capabilities
18
+
19
+ - Base-ref UX-impact PR lint with literal kill switch and separate error classes.
20
+ - `assertPlainEnglish`, `assertHonestFailure`, and `assertNoZombie` helpers.
21
+ - CI wiring for the assertions in `messaging-multi-agent.test.ts`.
@@ -0,0 +1,18 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ The UX PR gate now scopes enforcement to commit authors/committers and the branch pusher, verifies refactor-only exemptions deterministically, publishes an exemption-rate artifact, and blocks merge safety on internal lint errors. ELI16 companion generation no longer creates `.eli16.eli16.md` duplicates.
9
+
10
+ ## What to Tell Your User
11
+
12
+ Pull requests now show whether a UX lint decision was in scope, whether a refactor exemption was proven, and whether the result was safe to auto-merge. Internal uncertainty stops auto-merge instead of being silently accepted.
13
+
14
+ ## Summary of New Capabilities
15
+
16
+ - Author-scope union and deterministic refactor-only verification for UX lint.
17
+ - Per-run exemption-rate artifact for the weekly retro.
18
+ - Internal-error merge-safety gate and corrected ELI16 companion naming.
@@ -0,0 +1,9 @@
1
+ # Side-effects review: UX-first enforcement increment 1
2
+
3
+ - Adds a separate pull-request workflow and path allowlist; it reads the base-ref diff and PR body only.
4
+ - A missing or shallow UX declaration is a deterministic violation; unexpected tool failure is a distinct fail-open internal error with a loud annotation.
5
+ - Adds three pure assertions with no network, subprocess, or LLM dependency and wires them into the real messaging E2E scenario.
6
+ - `assertTimely` and content-sniff/ownership annotation work remain explicitly deferred to Increment 2.
7
+ - Rollback is removing the workflow/script/helper imports; no durable migration is required.
8
+ - The substance-floor matcher accepts single-, double-, and backtick-quoted diff strings so normal source quoting cannot create a false negative.
9
+ - The workflow also runs the messaging E2E in CI, making the three deterministic assertions an enforced, visible check.
@@ -0,0 +1,7 @@
1
+ # Side-effects review: UX-first enforcement increment 2
2
+
3
+ - Extends the deterministic PR lint with author/committer plus pusher scope, a verified refactor-only exemption, and a per-run exemption-rate artifact.
4
+ - Internal lint errors remain distinct and now make the merge-safety job fail, so native auto-merge cannot arm on an uncertain verdict.
5
+ - Fixes ELI16 companion resolution so an existing `.eli16.md` spec is never given a double suffix; historical duplicate companions are removed.
6
+ - No runtime user-facing behavior changes; proof is the real GitHub PR workflow and its uploaded artifact.
7
+ - The configured author scope is literal and reviewed alongside the kill switch; a pusher outside it is out of scope.