@sentropic/focus 0.3.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Fabien Antoine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @sentropic/focus
2
+
3
+ A **focused-session document runtime** — the read-only **FocusSnapshot** render-core for a
4
+ *Focus*: a focused working session on a subject, rendered as a renderable, multi-surface document
5
+ to orient/steer it (decide being one modality among orient / amend / comment). See
6
+ `spec/SPEC_VOL_FOCUS.md`.
7
+
8
+ This package is **private** (`"private": true`) and **app-local-first**: it is not yet published.
9
+ It is extracted to `@sentropic/focus` on npm only once a real external consumer wires it
10
+ (repo real-consumption rule).
11
+
12
+ ## Scope — Focus-M1 L1 (`feat/focus-render-core`)
13
+
14
+ - The **concrete `DecisionDossierDocument` model** — the decision-dossier is the *first* focus
15
+ type, not a generic Focus platform. Node families: `prose`, `question` (q + recommended
16
+ answer/préco + validation state), `optionSet`/`option` (accept|reject|comment annotation),
17
+ `outcome` (modality-tagged), `amendmentTrace`, `diagram`. Every node has a stable id + a
18
+ `targetRef`.
19
+ - The **three deterministic renderers**: `renderTerminal`, `renderMd`, `renderHtml` — **HTML is
20
+ mandatory**. Each takes a document and returns a `string`.
21
+ - A local **`DecisionDossierView`-shaped fixture type** + `toDecisionDossierDocument(view)` mapper.
22
+ L2 (`feat/focus-track-read`) rebinds the same mapper to the real `@sentropic/track/read`.
23
+
24
+ This is the **read-only FocusSnapshot** split: affordances render as **disabled metadata only**
25
+ (no live commands). Live drivers (`FocusLiveSession`) are deferred to later lots.
26
+
27
+ ## Injection + sanitize hooks (no bundled markdown/diagram engine)
28
+
29
+ Markdown is rendered by **injection**: the HTML renderer takes a host-supplied
30
+ `renderMarkdown(md) => string` hook (the package carries **no `marked`** dependency and **no
31
+ mdast/rehype core**). HTML is sanitized via a host-supplied `sanitizeHtml(html) => string` hook —
32
+ the renderer core owns structure only; hosts own sanitization and styling. The MD renderer emits
33
+ prose markdown verbatim by default, with an optional `renderMarkdown` hook for normalization.
34
+
35
+ **Diagrams = fenced fallback only** (no diagram adapter): a ` ```mermaid ` block in MD, a `<pre>`
36
+ in HTML, indented text in the terminal.
37
+
38
+ ## Usage
39
+
40
+ ```ts
41
+ import {
42
+ toDecisionDossierDocument,
43
+ renderTerminal,
44
+ renderMd,
45
+ renderHtml,
46
+ } from "@sentropic/focus";
47
+
48
+ const doc = toDecisionDossierDocument(view); // view = DecisionDossierView fixture (L1) / track read (L2)
49
+
50
+ const text = renderTerminal(doc);
51
+ const md = renderMd(doc);
52
+ const html = renderHtml(doc, {
53
+ renderMarkdown: (md) => marked.parse(md), // host supplies marked
54
+ sanitizeHtml: (html) => DOMPurify.sanitize(html), // host supplies the sanitizer
55
+ });
56
+ ```
57
+
58
+ ## Build / checks
59
+
60
+ ```
61
+ make typecheck-focus
62
+ make test-focus
63
+ make build-focus
64
+ make pack-focus
65
+ ```
@@ -0,0 +1,45 @@
1
+ /**
2
+ * `@sentropic/focus/cli` — the headless read-only Focus CLI driver (Focus-M1 L3).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §4b L3: the FIRST usable end-to-end dogfood. Reads a REAL track decision
5
+ * dossier (via the L2 `/track` binding `readDecisionDossier`) and renders it READ-ONLY to one of
6
+ * the three deterministic surfaces (terminal / MD / HTML — HTML mandatory). It is wired into the
7
+ * `stp` umbrella CLI as the in-repo `focus` subcommand (the manifest stays cross-repo-only).
8
+ *
9
+ * This module exports the federated-subcommand shape `{ run, version }` so the `stp` composition
10
+ * root can register it through the same typed contract the cross-repo federation loader uses. The
11
+ * `run` resolves to a process exit code (0 = success; non-zero with a clear stderr message on error
12
+ * — missing args, decision not found, contract mismatch). It is READ-ONLY: NO track write, NO new
13
+ * track event (those are L4).
14
+ *
15
+ * stp focus <decision-id> [--format terminal|md|html] [--workspace <ws>]
16
+ * [--baseline-commit <sha>] [--events-path <path>]
17
+ *
18
+ * Defaults: `--events-path` = `.track/events.jsonl`, `--format` = `terminal`. `--workspace` is
19
+ * required (a decision lives in a per-workspace canevas; there is no safe default).
20
+ */
21
+ /** The `{ run, version }` contract surfaced to the `stp` subcommand registry. */
22
+ export declare const version: string;
23
+ /**
24
+ * IO sinks for {@link run}. Defaults wire `process.stdout`/`process.stderr` so the CLI is testable
25
+ * without capturing the global streams (the federation loader uses the same injectable-deps style).
26
+ */
27
+ export interface FocusCliDeps {
28
+ /** Where rendered output goes (default: stdout). */
29
+ readonly out?: (text: string) => void;
30
+ /** Where error messages go (default: stderr). */
31
+ readonly error?: (text: string) => void;
32
+ }
33
+ /**
34
+ * Run the read-only Focus CLI. Returns a process exit code:
35
+ * 0 — rendered successfully;
36
+ * 2 — usage error (bad/missing args; also `--help`);
37
+ * 3 — the decision was not found in the read log;
38
+ * 4 — the installed `@sentropic/track/read` contract major is incompatible;
39
+ * 1 — any other read/render failure.
40
+ *
41
+ * READ-ONLY: it never writes a track event. The `readAt` timestamp is captured here (the CLI is the
42
+ * clock boundary; the L2 binding itself is clockless).
43
+ */
44
+ export declare const run: (argv: readonly string[], deps?: FocusCliDeps) => Promise<number>;
45
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAiBH,iFAAiF;AACjF,eAAO,MAAM,OAAO,EAAE,MAAoB,CAAC;AA2G3C;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAiBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,GAAG,SACR,SAAS,MAAM,EAAE,SACjB,YAAY,KACjB,QAAQ,MAAM,CAkDhB,CAAC"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `@sentropic/focus/cli` — the headless read-only Focus CLI driver (Focus-M1 L3).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §4b L3: the FIRST usable end-to-end dogfood. Reads a REAL track decision
5
+ * dossier (via the L2 `/track` binding `readDecisionDossier`) and renders it READ-ONLY to one of
6
+ * the three deterministic surfaces (terminal / MD / HTML — HTML mandatory). It is wired into the
7
+ * `stp` umbrella CLI as the in-repo `focus` subcommand (the manifest stays cross-repo-only).
8
+ *
9
+ * This module exports the federated-subcommand shape `{ run, version }` so the `stp` composition
10
+ * root can register it through the same typed contract the cross-repo federation loader uses. The
11
+ * `run` resolves to a process exit code (0 = success; non-zero with a clear stderr message on error
12
+ * — missing args, decision not found, contract mismatch). It is READ-ONLY: NO track write, NO new
13
+ * track event (those are L4).
14
+ *
15
+ * stp focus <decision-id> [--format terminal|md|html] [--workspace <ws>]
16
+ * [--baseline-commit <sha>] [--events-path <path>]
17
+ *
18
+ * Defaults: `--events-path` = `.track/events.jsonl`, `--format` = `terminal`. `--workspace` is
19
+ * required (a decision lives in a per-workspace canevas; there is no safe default).
20
+ */
21
+ import { createRequire } from "node:module";
22
+ import { renderHtml, renderMd, renderTerminal } from "../index.js";
23
+ import { DecisionNotFoundError, TrackContractMismatchError, readDecisionDossier, } from "../track/index.js";
24
+ /** The package version, read from the package manifest (single source of truth). */
25
+ const pkg = createRequire(import.meta.url)("../../package.json");
26
+ /** The `{ run, version }` contract surfaced to the `stp` subcommand registry. */
27
+ export const version = pkg.version;
28
+ /** The supported render formats. */
29
+ const FORMATS = ["terminal", "md", "html"];
30
+ const DEFAULT_EVENTS_PATH = ".track/events.jsonl";
31
+ const DEFAULT_FORMAT = "terminal";
32
+ /** A parse failure carrying the message to print on stderr (exit code 2 = usage error). */
33
+ class UsageError extends Error {
34
+ }
35
+ const USAGE = "Usage: stp focus <decision-id> [--format terminal|md|html] " +
36
+ "[--workspace <ws>] [--baseline-commit <sha>] [--events-path <path>]\n" +
37
+ "Render a track decision dossier read-only. Defaults: --format terminal, " +
38
+ `--events-path ${DEFAULT_EVENTS_PATH}. --workspace is required.`;
39
+ /** Read the single value that must follow a `--flag`, or fail with a usage error. */
40
+ const takeValue = (argv, i, flag) => {
41
+ const value = argv[i + 1];
42
+ if (value === undefined || value.startsWith("--")) {
43
+ throw new UsageError(`Missing value for ${flag}.`);
44
+ }
45
+ return value;
46
+ };
47
+ /**
48
+ * Parse the argv that follows `focus` (so `stp focus D1 --format md` → `['D1','--format','md']`).
49
+ * Throws `UsageError` (with `--help` text) on a malformed invocation.
50
+ */
51
+ const parseArgs = (argv) => {
52
+ const positionals = [];
53
+ let format = DEFAULT_FORMAT;
54
+ let workspace;
55
+ let baselineCommit = "";
56
+ let eventsPath = DEFAULT_EVENTS_PATH;
57
+ for (let i = 0; i < argv.length; i += 1) {
58
+ const arg = argv[i];
59
+ if (arg === undefined)
60
+ continue;
61
+ switch (arg) {
62
+ case "-h":
63
+ case "--help":
64
+ throw new UsageError(USAGE);
65
+ case "--format": {
66
+ const value = takeValue(argv, i, "--format");
67
+ if (!FORMATS.includes(value)) {
68
+ throw new UsageError(`Unknown --format "${value}" (expected one of: ${FORMATS.join(", ")}).`);
69
+ }
70
+ format = value;
71
+ i += 1;
72
+ break;
73
+ }
74
+ case "--workspace":
75
+ workspace = takeValue(argv, i, "--workspace");
76
+ i += 1;
77
+ break;
78
+ case "--baseline-commit":
79
+ baselineCommit = takeValue(argv, i, "--baseline-commit");
80
+ i += 1;
81
+ break;
82
+ case "--events-path":
83
+ eventsPath = takeValue(argv, i, "--events-path");
84
+ i += 1;
85
+ break;
86
+ default:
87
+ if (arg.startsWith("--")) {
88
+ throw new UsageError(`Unknown option "${arg}".\n${USAGE}`);
89
+ }
90
+ positionals.push(arg);
91
+ break;
92
+ }
93
+ }
94
+ if (positionals.length === 0) {
95
+ throw new UsageError(`Missing <decision-id>.\n${USAGE}`);
96
+ }
97
+ if (positionals.length > 1) {
98
+ throw new UsageError(`Expected exactly one <decision-id>, got ${positionals.length}.\n${USAGE}`);
99
+ }
100
+ if (workspace === undefined || workspace.trim() === "") {
101
+ throw new UsageError(`Missing --workspace (required).\n${USAGE}`);
102
+ }
103
+ return {
104
+ decisionId: positionals[0],
105
+ format,
106
+ workspace,
107
+ baselineCommit,
108
+ eventsPath,
109
+ };
110
+ };
111
+ /**
112
+ * Default render hooks for the headless CLI. The HTML surface needs a markdown converter + a
113
+ * sanitizer; the headless dogfood does NOT bundle `marked`/DOMPurify (M0 injection rule), so the
114
+ * default emits prose markdown inside a `<pre>` and is a no-op sanitizer — hosts that want rich
115
+ * HTML inject their own hooks. The MD/terminal surfaces never touch these.
116
+ */
117
+ const defaultHtmlHooks = {
118
+ renderMarkdown: (md) => `<pre class="focus-md-raw">${md
119
+ .replace(/&/g, "&amp;")
120
+ .replace(/</g, "&lt;")
121
+ .replace(/>/g, "&gt;")}</pre>`,
122
+ sanitizeHtml: (html) => html,
123
+ };
124
+ /**
125
+ * Run the read-only Focus CLI. Returns a process exit code:
126
+ * 0 — rendered successfully;
127
+ * 2 — usage error (bad/missing args; also `--help`);
128
+ * 3 — the decision was not found in the read log;
129
+ * 4 — the installed `@sentropic/track/read` contract major is incompatible;
130
+ * 1 — any other read/render failure.
131
+ *
132
+ * READ-ONLY: it never writes a track event. The `readAt` timestamp is captured here (the CLI is the
133
+ * clock boundary; the L2 binding itself is clockless).
134
+ */
135
+ export const run = async (argv, deps = {}) => {
136
+ const out = deps.out ?? ((text) => process.stdout.write(text));
137
+ const error = deps.error ?? ((text) => process.stderr.write(`${text}\n`));
138
+ let parsed;
139
+ try {
140
+ parsed = parseArgs(argv);
141
+ }
142
+ catch (err) {
143
+ if (err instanceof UsageError) {
144
+ error(err.message);
145
+ return 2;
146
+ }
147
+ throw err;
148
+ }
149
+ try {
150
+ const doc = readDecisionDossier(parsed.eventsPath, {
151
+ workspace: parsed.workspace,
152
+ baselineCommit: parsed.baselineCommit,
153
+ decisionId: parsed.decisionId,
154
+ }, new Date().toISOString());
155
+ switch (parsed.format) {
156
+ case "terminal":
157
+ out(renderTerminal(doc));
158
+ break;
159
+ case "md":
160
+ out(renderMd(doc));
161
+ break;
162
+ case "html":
163
+ out(renderHtml(doc, defaultHtmlHooks));
164
+ break;
165
+ }
166
+ return 0;
167
+ }
168
+ catch (err) {
169
+ if (err instanceof DecisionNotFoundError) {
170
+ error(err.message);
171
+ return 3;
172
+ }
173
+ if (err instanceof TrackContractMismatchError) {
174
+ error(err.message);
175
+ return 4;
176
+ }
177
+ error(`focus: ${err instanceof Error ? err.message : String(err)}`);
178
+ return 1;
179
+ }
180
+ };
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @sentropic/focus (Focus-M1 L2) — the FocusSnapshot render-core.
3
+ *
4
+ * Public API: the concrete decision-dossier document model, the three deterministic renderers
5
+ * (terminal / MD / HTML — read-only snapshot), and the host render hooks (markdown injection +
6
+ * HTML sanitization). The `/track` subpath (`@sentropic/focus/track`) binds the model to the REAL
7
+ * `@sentropic/track/read` `DecisionDossierView` + `amendmentTrace` (L2 replaced L1's local
8
+ * `DecisionDossierViewFixture` type + mapper).
9
+ */
10
+ export type { Affordance, AmendmentStep, AmendmentTraceNode, ComprehensionEvidence, DecisionDossierDocument, DiagramNode, DiagramSyntax, FocusNode, FocusProvenance, FocusRef, OptionAnnotationState, OptionNode, OptionSetNode, OutcomeModality, OutcomeNode, ProseNode, QuestionNode, QuestionValidationState, TargetRef, } from "./model.js";
11
+ export type { HtmlRenderHooks, MdRenderHooks, RenderMarkdown, SanitizeHtml, } from "./render/hooks.js";
12
+ export { renderTerminal } from "./render/terminal.js";
13
+ export type { TerminalRenderOptions } from "./render/terminal.js";
14
+ export { renderMd } from "./render/md.js";
15
+ export { renderHtml } from "./render/html.js";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,YAAY,EACV,UAAU,EACV,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,QAAQ,EACR,qBAAqB,EACrB,UAAU,EACV,aAAa,EACb,eAAe,EACf,WAAW,EACX,SAAS,EACT,YAAY,EACZ,uBAAuB,EACvB,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,eAAe,EACf,aAAa,EACb,cAAc,EACd,YAAY,GACb,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @sentropic/focus (Focus-M1 L2) — the FocusSnapshot render-core.
3
+ *
4
+ * Public API: the concrete decision-dossier document model, the three deterministic renderers
5
+ * (terminal / MD / HTML — read-only snapshot), and the host render hooks (markdown injection +
6
+ * HTML sanitization). The `/track` subpath (`@sentropic/focus/track`) binds the model to the REAL
7
+ * `@sentropic/track/read` `DecisionDossierView` + `amendmentTrace` (L2 replaced L1's local
8
+ * `DecisionDossierViewFixture` type + mapper).
9
+ */
10
+ export { renderTerminal } from "./render/terminal.js";
11
+ export { renderMd } from "./render/md.js";
12
+ export { renderHtml } from "./render/html.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA+BH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Focus document model — the CONCRETE decision-dossier specialization (Focus-M1 L1).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §3: a Focus is a renderable + interactive document over track data.
5
+ * This file models the `DecisionDossierDocument` — the FIRST focus TYPE (a FocusDocument
6
+ * whose primary outcome modality is a decision). It is intentionally CONCRETE, not a generic
7
+ * "Focus platform": the model stays decision-dossier-shaped until a 2nd modality is real.
8
+ *
9
+ * This is the read-only FocusSnapshot split: affordances render as DISABLED metadata only
10
+ * (no live commands). Live drivers (FocusLiveSession) are deferred (L3+).
11
+ */
12
+ /**
13
+ * A stable reference to the source entity this document projects (e.g. a track decision id).
14
+ * Opaque at L1 (fixture-driven); L2 binds it to the real track `DecisionDossierView`.
15
+ */
16
+ export type FocusRef = string;
17
+ /**
18
+ * A stable reference to a rendered element — every rendered node is an interaction target.
19
+ * At L1 these are inert (affordances are disabled metadata); they exist so L3+ live drivers
20
+ * can route commands without re-deriving identity.
21
+ */
22
+ export type TargetRef = string;
23
+ /** Outcome modalities. The decision-dossier's primary modality is `decision`. */
24
+ export type OutcomeModality = "decision" | "orientation" | "amendment" | "comment";
25
+ /** Diagram syntaxes. Diagrams render as a FENCED fallback only at L1 (no diagram adapter). */
26
+ export type DiagramSyntax = "mermaid" | "plantuml";
27
+ /** Q/R validation state for a `question` node (recommendation = préco). */
28
+ export type QuestionValidationState = "open" | "answered" | "validated";
29
+ /** Annotation state for an `option` node (Claude-style accept/reject/comment). */
30
+ export type OptionAnnotationState = "none" | "accept" | "reject" | "comment";
31
+ /** Base fields shared by every rendered node: a stable id + an interaction target ref. */
32
+ interface FocusNodeBase {
33
+ readonly id: string;
34
+ readonly targetRef: TargetRef;
35
+ }
36
+ /** Markdown prose body. The renderer injects the markdown→string conversion (host hook). */
37
+ export interface ProseNode extends FocusNodeBase {
38
+ readonly kind: "prose";
39
+ readonly markdown: string;
40
+ }
41
+ /** A question with a recommended answer (préco) and a validation state. */
42
+ export interface QuestionNode extends FocusNodeBase {
43
+ readonly kind: "question";
44
+ readonly question: string;
45
+ /** The recommended answer (préco). Optional — a question may be open with no préco yet. */
46
+ readonly recommendedAnswer?: string;
47
+ /** The actual answer, when answered. */
48
+ readonly answer?: string;
49
+ readonly state: QuestionValidationState;
50
+ }
51
+ /** A single option/proposal/alternative with a Claude-style annotation state. */
52
+ export interface OptionNode extends FocusNodeBase {
53
+ readonly kind: "option";
54
+ readonly label: string;
55
+ readonly body?: string;
56
+ readonly annotation: OptionAnnotationState;
57
+ /** Optional free-text comment carried with a `comment` annotation. */
58
+ readonly annotationComment?: string;
59
+ }
60
+ /** A set of mutually-related options/proposals. */
61
+ export interface OptionSetNode extends FocusNodeBase {
62
+ readonly kind: "optionSet";
63
+ readonly title?: string;
64
+ readonly options: readonly OptionNode[];
65
+ }
66
+ /** The modality-tagged outcome of the focus. */
67
+ export interface OutcomeNode extends FocusNodeBase {
68
+ readonly kind: "outcome";
69
+ readonly modality: OutcomeModality;
70
+ readonly statement: string;
71
+ }
72
+ /** One ordered step of the amendment trace. */
73
+ export interface AmendmentStep {
74
+ readonly at: string;
75
+ readonly author: string;
76
+ readonly summary: string;
77
+ }
78
+ /** The ordered amendment trace, rendered as a section. */
79
+ export interface AmendmentTraceNode extends FocusNodeBase {
80
+ readonly kind: "amendmentTrace";
81
+ readonly steps: readonly AmendmentStep[];
82
+ }
83
+ /** A diagram. At L1 this renders as a fenced fallback only (no diagram adapter). */
84
+ export interface DiagramNode extends FocusNodeBase {
85
+ readonly kind: "diagram";
86
+ readonly syntax: DiagramSyntax;
87
+ readonly source: string;
88
+ readonly alt: string;
89
+ }
90
+ /** The node families needed to render a decision-dossier. */
91
+ export type FocusNode = ProseNode | QuestionNode | OptionSetNode | OutcomeNode | AmendmentTraceNode | DiagramNode;
92
+ /**
93
+ * An affordance = a potential interaction surfaced on the snapshot. At L1 these are DISABLED
94
+ * metadata only (read-only FocusSnapshot): no live command is submitted. L3+ FocusLiveSession
95
+ * maps an affordance name → a track WorkEventKind. Never invent kinds.
96
+ */
97
+ export interface Affordance {
98
+ readonly name: string;
99
+ /** The target this affordance would act on (a node's targetRef). */
100
+ readonly targetRef: TargetRef;
101
+ /** Human-readable label for the disabled-affordance metadata. */
102
+ readonly label: string;
103
+ /**
104
+ * Always `false` at L1 — the snapshot is read-only. Kept explicit so renderers print the
105
+ * disabled state and L3+ can flip it for live sessions.
106
+ */
107
+ readonly enabled: false;
108
+ }
109
+ /**
110
+ * Comprehension evidence carried VERBATIM from the dossier artifacts (subject/dossierHash/sig
111
+ * intact, NOT re-derived lineage — the confused-deputy fix). At L1 it is opaque provenance.
112
+ */
113
+ export interface ComprehensionEvidence {
114
+ readonly subject: string;
115
+ readonly dossierHash: string;
116
+ readonly signature?: string;
117
+ }
118
+ /** Provenance of the projection (where the data came from + when it was read). */
119
+ export interface FocusProvenance {
120
+ readonly source: string;
121
+ readonly readAt: string;
122
+ readonly comprehensionEvidence?: ComprehensionEvidence;
123
+ }
124
+ /**
125
+ * The CONCRETE decision-dossier document — a FocusDocument whose primary outcome modality is a
126
+ * decision. Maps to track `DecisionDossierView{outcome,dossier,comprehensionEvidence}` +
127
+ * `amendmentTrace` (wired for real at L2).
128
+ */
129
+ export interface DecisionDossierDocument {
130
+ readonly ref: FocusRef;
131
+ readonly subject: string;
132
+ readonly title: string;
133
+ /** OCC base hash for the snapshot — used by L3+ reconcile; inert metadata at L1. */
134
+ readonly hash: string;
135
+ /** Read cursor / version marker from the source. */
136
+ readonly cursor: string;
137
+ readonly sections: readonly FocusNode[];
138
+ /** Disabled-affordance metadata (read-only snapshot). */
139
+ readonly interactions: readonly Affordance[];
140
+ readonly provenance: FocusProvenance;
141
+ readonly amendmentTrace: readonly AmendmentStep[];
142
+ }
143
+ export {};
144
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"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"}
package/dist/model.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Focus document model — the CONCRETE decision-dossier specialization (Focus-M1 L1).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §3: a Focus is a renderable + interactive document over track data.
5
+ * This file models the `DecisionDossierDocument` — the FIRST focus TYPE (a FocusDocument
6
+ * whose primary outcome modality is a decision). It is intentionally CONCRETE, not a generic
7
+ * "Focus platform": the model stays decision-dossier-shaped until a 2nd modality is real.
8
+ *
9
+ * This is the read-only FocusSnapshot split: affordances render as DISABLED metadata only
10
+ * (no live commands). Live drivers (FocusLiveSession) are deferred (L3+).
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../src/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Host-supplied render hooks (Focus-M1 L1).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §4 (M0 constraint #2): markdown is rendered by INJECTION — the host
5
+ * supplies the markdown→HTML conversion (e.g. `marked`). The focus package carries NO `marked`
6
+ * dependency and NO mdast/rehype core. HTML is sanitized via a host-supplied hook; the renderer
7
+ * core owns structure only, hosts own sanitization/styling.
8
+ */
9
+ /** Convert markdown source to an HTML string. Host-supplied (e.g. `marked`). */
10
+ export type RenderMarkdown = (markdown: string) => string;
11
+ /** Sanitize an HTML string before emission. Host-supplied (e.g. DOMPurify / sanitize-html). */
12
+ export type SanitizeHtml = (html: string) => string;
13
+ /** Hooks the HTML renderer needs from the host. */
14
+ export interface HtmlRenderHooks {
15
+ readonly renderMarkdown: RenderMarkdown;
16
+ readonly sanitizeHtml: SanitizeHtml;
17
+ }
18
+ /** Hooks the MD renderer needs from the host. */
19
+ export interface MdRenderHooks {
20
+ /**
21
+ * MD output is markdown; prose nodes already carry markdown verbatim, so a renderMarkdown
22
+ * hook is OPTIONAL for MD (used only if the host wants to normalize/transform the prose).
23
+ * When omitted, prose markdown is emitted as-is.
24
+ */
25
+ readonly renderMarkdown?: RenderMarkdown;
26
+ }
27
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/render/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,gFAAgF;AAChF,MAAM,MAAM,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;AAE1D,+FAA+F;AAC/F,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAEpD,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;CAC1C"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Host-supplied render hooks (Focus-M1 L1).
3
+ *
4
+ * Per SPEC_VOL_FOCUS §4 (M0 constraint #2): markdown is rendered by INJECTION — the host
5
+ * supplies the markdown→HTML conversion (e.g. `marked`). The focus package carries NO `marked`
6
+ * dependency and NO mdast/rehype core. HTML is sanitized via a host-supplied hook; the renderer
7
+ * core owns structure only, hosts own sanitization/styling.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/render/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * HTML renderer (Focus-M1 L1) — MANDATORY surface.
3
+ *
4
+ * Renders a `DecisionDossierDocument` to an HTML string. Read-only FocusSnapshot:
5
+ * affordances appear as DISABLED metadata; diagrams fall back to a `<pre>` block (no diagram
6
+ * adapter). Markdown prose is converted via the host-supplied `renderMarkdown` hook (NO `marked`
7
+ * dep, NO mdast core); the full document HTML is run through the host `sanitizeHtml` hook before
8
+ * emission. The renderer core owns structure only; hosts own sanitization/styling.
9
+ */
10
+ import type { DecisionDossierDocument } from "../model.js";
11
+ import type { HtmlRenderHooks } from "./hooks.js";
12
+ /** Render a decision-dossier document to a sanitized HTML string. */
13
+ export declare const renderHtml: (doc: DecisionDossierDocument, hooks: HtmlRenderHooks) => string;
14
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/render/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAGV,uBAAuB,EAExB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA2FlD,qEAAqE;AACrE,eAAO,MAAM,UAAU,QAChB,uBAAuB,SACrB,eAAe,KACrB,MAkBF,CAAC"}