@hypercarrier/rarebit 0.5.2 → 0.6.0
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.
- package/CHANGELOG.md +7 -0
- package/VISUAL-LANGUAGE.md +5 -5
- package/package.json +1 -1
- package/src/rarebit-visual-language.mjs +6 -10
- package/src/types.d.ts +0 -2
- package/test/automatic-summary-policy.test.mjs +1 -0
- package/test/extension-activity.test.mjs +1 -0
- package/test/herdr-activity.test.mjs +1 -0
- package/test/home-guard.test.mjs +26 -0
- package/test/home.mjs +16 -0
- package/test/lifecycle-contract-independent.test.mjs +1 -0
- package/test/package-artifact-identity.test.mjs +1 -0
- package/test/piq.test.mjs +1 -0
- package/test/rarebit-activity.test.mjs +1 -0
- package/test/rarebit-artifact-state.test.mjs +1 -0
- package/test/rarebit-btw.test.mjs +1 -0
- package/test/rarebit-cli.test.mjs +1 -0
- package/test/rarebit-command.test.mjs +1 -0
- package/test/rarebit-core.test.mjs +1 -0
- package/test/rarebit-cross-consumer-conformance.test.mjs +1 -0
- package/test/rarebit-fork.test.mjs +1 -0
- package/test/rarebit-independent-contract.test.mjs +1 -0
- package/test/rarebit-recall.test.mjs +1 -0
- package/test/rarebit-recap.test.mjs +1 -0
- package/test/rarebit-settings-ui.test.mjs +2 -1
- package/test/rarebit-sidecar-protocol.test.mjs +1 -0
- package/test/rarebit-v020.test.mjs +1 -0
- package/test/rarebit-view.test.mjs +1 -0
- package/test/rarebit-visual-language.test.mjs +7 -6
- package/test/recap-read-checkpoint.test.mjs +1 -0
- package/test/simulator-selection.test.mjs +1 -0
- package/test/summary-attention-independent.test.mjs +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 — 2026-10-29
|
|
4
|
+
|
|
5
|
+
- Retire the attention presentation of the `needs_attention` Summary status.
|
|
6
|
+
It now reads as an ordinary Summary: no `◆!` mark, no "needs you" label, and
|
|
7
|
+
no attention tone. The status and its classifier stay unchanged. The Recap
|
|
8
|
+
heading reads `Recap·summarized·…`.
|
|
9
|
+
|
|
3
10
|
## 0.5.2 — 2026-10-15
|
|
4
11
|
|
|
5
12
|
- Add a development-only simulator lane (`npm run test:sim`) that runs real
|
package/VISUAL-LANGUAGE.md
CHANGED
|
@@ -61,9 +61,9 @@ The scopes are deliberately different: Session-history events are repeatable, ti
|
|
|
61
61
|
</tr>
|
|
62
62
|
<tr>
|
|
63
63
|
<td><code>needs_attention</code></td>
|
|
64
|
-
<td><span style="color: #
|
|
65
|
-
<td
|
|
66
|
-
<td>
|
|
64
|
+
<td><span style="color: #334155">summarized</span></td>
|
|
65
|
+
<td>summarized</td>
|
|
66
|
+
<td>ordinary</td>
|
|
67
67
|
</tr>
|
|
68
68
|
<tr>
|
|
69
69
|
<td><code>ineligible</code></td>
|
|
@@ -82,8 +82,8 @@ The scopes are deliberately different: Session-history events are repeatable, ti
|
|
|
82
82
|
|
|
83
83
|
There is one user-message category, and every selected user message uses the same visual role. The smaller solid continuation dot means work continues. The larger solid stop circle marks a reported response boundary, not verified completion. Supporting intervals paint first, then continuations, stops and errors, and user messages last. Selection adds a halo without replacing the canonical mark.
|
|
84
84
|
|
|
85
|
-
Summary reasons such as `decision`, `blocker`, or `unfinished` are secondary text, never new colors or shapes. Summary prose uses normal body styling.
|
|
85
|
+
Summary reasons such as `decision`, `blocker`, or `unfinished` are secondary text, never new colors or shapes. Summary prose uses normal body styling. No Summary result has a high-salience mark, color, or sound. `needs_attention` is an ordinary result: show the Summary text, not an attention label. Owner attention comes from other sources, such as Arc asks and runtime facts. Finished has no green, checkmark, or success treatment because it is an appearance-level Session assessment, not proof that a Task, Project, tool run, or delivery succeeded. Error is diagnostic.
|
|
86
86
|
|
|
87
|
-
Synchronization and applicability qualify a result; they are not additional statuses. Append `· source pending` when applicable, expose detailed sync/applicability in an inspector
|
|
87
|
+
Synchronization and applicability qualify a result; they are not additional statuses. Append `· source pending` when applicable, expose detailed sync/applicability in an inspector. Never publish `syncing` as a Summary status. Consumers support only the current package projection and don't translate unsupported receipt shapes into visual meaning.
|
|
88
88
|
|
|
89
89
|
Every mark has an accessible text label. Do not rely on color, hover, or sound. A muted monochrome snapshot may use lighter weight or terminal `dim`, but its text must remain readable and meet WCAG 2.2 AA where applicable. Terminal renderers preserve the monochrome mark and label with the closest theme-safe ANSI treatment. Consumers import the package mapping rather than inventing another one, cite this file for perceptual intent, and keep application layout local.
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Executable projection of the perceptual contract in ../VISUAL-LANGUAGE.md.
|
|
2
2
|
// Consumers own layout and renderer-specific styling; this module owns semantic
|
|
3
|
-
// marks, labels, tones, and source-pending
|
|
3
|
+
// marks, labels, tones, and the source-pending qualifier. No Summary status has an
|
|
4
|
+
// attention presentation: `needs_attention` reads as an ordinary Summary.
|
|
4
5
|
|
|
5
6
|
export const RAREBIT_EVENT_PRESENTATION = Object.freeze({
|
|
6
7
|
user_message: Object.freeze({
|
|
@@ -43,10 +44,10 @@ export const RAREBIT_SUMMARY_PRESENTATION = Object.freeze({
|
|
|
43
44
|
salience: "ordinary",
|
|
44
45
|
}),
|
|
45
46
|
needs_attention: Object.freeze({
|
|
46
|
-
mark:
|
|
47
|
-
label: "
|
|
48
|
-
tone: "
|
|
49
|
-
salience: "
|
|
47
|
+
mark: null,
|
|
48
|
+
label: "summarized",
|
|
49
|
+
tone: "neutral",
|
|
50
|
+
salience: "ordinary",
|
|
50
51
|
}),
|
|
51
52
|
ineligible: Object.freeze({
|
|
52
53
|
mark: null,
|
|
@@ -101,10 +102,5 @@ export function rarebitSummaryPresentation(
|
|
|
101
102
|
...presentation,
|
|
102
103
|
mark: null,
|
|
103
104
|
label: `${presentation.label} · source pending`,
|
|
104
|
-
tone: presentation.tone === "attention" ? "neutral" : presentation.tone,
|
|
105
|
-
salience:
|
|
106
|
-
presentation.salience === "attention"
|
|
107
|
-
? "ordinary"
|
|
108
|
-
: presentation.salience,
|
|
109
105
|
});
|
|
110
106
|
}
|
package/src/types.d.ts
CHANGED
|
@@ -199,7 +199,6 @@ export type RarebitVisualTone =
|
|
|
199
199
|
| "user"
|
|
200
200
|
| "continuation"
|
|
201
201
|
| "boundary"
|
|
202
|
-
| "attention"
|
|
203
202
|
| "diagnostic"
|
|
204
203
|
| "muted";
|
|
205
204
|
export type RarebitEventKind =
|
|
@@ -213,7 +212,6 @@ export type RarebitVisualPresentation = {
|
|
|
213
212
|
| "smaller"
|
|
214
213
|
| "larger"
|
|
215
214
|
| "ordinary"
|
|
216
|
-
| "attention"
|
|
217
215
|
| "muted"
|
|
218
216
|
| "diagnostic";
|
|
219
217
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "./home.mjs";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import { Worker } from "node:worker_threads";
|
|
7
|
+
|
|
8
|
+
const root = join(import.meta.dirname, "..");
|
|
9
|
+
|
|
10
|
+
test("HOME guard: every test file imports test/home.mjs first", () => {
|
|
11
|
+
const missing = [];
|
|
12
|
+
for (const dir of ["test", "sim"]) for (const name of readdirSync(join(root, dir))) {
|
|
13
|
+
if (!name.endsWith(".test.mjs")) continue;
|
|
14
|
+
const first = readFileSync(join(root, dir, name), "utf8").match(/^import\b.*$/m)?.[0];
|
|
15
|
+
if (first !== `import "${dir === "test" ? "./home.mjs" : "../test/home.mjs"}";`) missing.push(`${dir}/${name}`);
|
|
16
|
+
}
|
|
17
|
+
assert.deepEqual(missing, []);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Bun caches os.homedir() at start, so a runtime HOME never reaches it. A worker thread is the same case under
|
|
21
|
+
// Node: its HOME stays in its own process.env copy.
|
|
22
|
+
test("HOME guard: a process whose os.homedir() does not follow HOME stops before any test code runs", async () => {
|
|
23
|
+
const worker = new Worker(new URL("./home.mjs", import.meta.url));
|
|
24
|
+
const error = await new Promise((resolve) => { worker.once("error", resolve); worker.once("exit", () => resolve(undefined)); });
|
|
25
|
+
assert.match(String(error), /HOME isolation failed: .* No test code ran\./);
|
|
26
|
+
});
|
package/test/home.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Test HOME isolation: import this module first in every test file, or load it as a Vitest setup file. It gives the
|
|
2
|
+
// test process a fresh HOME and proves that os.homedir() follows it before any test code runs. Bun caches
|
|
3
|
+
// os.homedir() at start, so a HOME set at runtime does not reach it; on 2026-10-29 a Bun test run wrote the
|
|
4
|
+
// operator's ~/.pi this way. scripts/verify-test-home.mjs keeps every copy of this file identical.
|
|
5
|
+
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
6
|
+
import { homedir, tmpdir, userInfo } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
const home = mkdtempSync(join(tmpdir(), "hc-test-home-"));
|
|
10
|
+
process.once("exit", () => rmSync(home, { recursive: true, force: true }));
|
|
11
|
+
process.env.HOME = home;
|
|
12
|
+
// Pi paths then default inside the fresh HOME; a test that needs an agent directory sets its own.
|
|
13
|
+
delete process.env.PI_CODING_AGENT_DIR;
|
|
14
|
+
const resolved = realpathSync(homedir()), operator = realpathSync(userInfo().homedir);
|
|
15
|
+
if (resolved !== realpathSync(home) || resolved === operator)
|
|
16
|
+
throw new Error(`HOME isolation failed: os.homedir() is ${resolved}, HOME is ${home}, the operator HOME is ${operator}. This runtime does not follow a HOME set at runtime; run the tests under Node. No test code ran.`);
|
package/test/piq.test.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./home.mjs";
|
|
1
2
|
import assert from "node:assert/strict";
|
|
2
3
|
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
@@ -192,7 +193,7 @@ test("recap renders the human label and converts the receipt timestamp to the co
|
|
|
192
193
|
const result = await controller.showExisting(context);
|
|
193
194
|
assert.equal(result.shown, true);
|
|
194
195
|
const heading = widgets[0]({ requestRender() {} }, { fg: (_name, text) => text }).render(200).find((line) => line.includes("Recap·")).trim();
|
|
195
|
-
assert.match(heading, /^
|
|
196
|
+
assert.match(heading, /^Recap·summarized·01\/01 08:00\(GMT\+8\)/);
|
|
196
197
|
assert.doesNotMatch(heading, /as of|2026|Asia\/Hong_Kong/);
|
|
197
198
|
assert.doesNotMatch(heading, /Rarebit Summary/);
|
|
198
199
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./home.mjs";
|
|
1
2
|
import test from "node:test";
|
|
2
3
|
import assert from "node:assert/strict";
|
|
3
4
|
import {
|
|
@@ -37,7 +38,7 @@ test("event presentation preserves the canonical occurrence grammar", () => {
|
|
|
37
38
|
assert.equal(rarebitEventPresentation("terminal_error").mark, "×");
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
test("Summary presentation keeps
|
|
41
|
+
test("Summary presentation keeps every outcome neutral, needs_attention included", () => {
|
|
41
42
|
assert.deepEqual(rarebitSummaryPresentation("finished"), {
|
|
42
43
|
mark: null,
|
|
43
44
|
label: "appears finished",
|
|
@@ -45,16 +46,16 @@ test("Summary presentation keeps ordinary outcomes neutral and attention singula
|
|
|
45
46
|
salience: "ordinary",
|
|
46
47
|
});
|
|
47
48
|
assert.deepEqual(rarebitSummaryPresentation("needs_attention"), {
|
|
48
|
-
mark:
|
|
49
|
-
label: "
|
|
50
|
-
tone: "
|
|
51
|
-
salience: "
|
|
49
|
+
mark: null,
|
|
50
|
+
label: "summarized",
|
|
51
|
+
tone: "neutral",
|
|
52
|
+
salience: "ordinary",
|
|
52
53
|
});
|
|
53
54
|
assert.deepEqual(
|
|
54
55
|
rarebitSummaryPresentation("needs_attention", { sourcePending: true }),
|
|
55
56
|
{
|
|
56
57
|
mark: null,
|
|
57
|
-
label: "
|
|
58
|
+
label: "summarized · source pending",
|
|
58
59
|
tone: "neutral",
|
|
59
60
|
salience: "ordinary",
|
|
60
61
|
},
|