@mmmnt/feat-adapter-model 0.0.0-stage → 0.1.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) 2026, Moment
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 CHANGED
@@ -1,3 +1,120 @@
1
- # Temporary Holding Version
1
+ # @mmmnt/feat-adapter-model
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Recorded-judge service adapter for [Feature](https://github.com/mmmnt/feature), the `.feat` execution
4
+ specification language. Use it for a **judgment port**: the part of your system that asks a language model to
5
+ decide something. A spec seeds the answers the judge gives and predicts the asks the system makes. Nothing goes
6
+ over a network and no model is ever called, so a run is repeatable, free and needs no key.
7
+
8
+ The adapter keeps the port's records in one JSON Lines file per namespace. Your system reads its recorded answers
9
+ from that file and appends its asks to it. The adapter is the same at every tier, because there is no product
10
+ behind it (ADR-0028, SPEC-ADPMODEL-001).
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npm install --save-dev @mmmnt/feat-adapter-model
16
+ ```
17
+
18
+ Requires Node 20 or later and `@mmmnt/feature` in the project. There are no runtime dependencies beyond
19
+ `@mmmnt/feat-types`.
20
+
21
+ ## Configuration
22
+
23
+ ```jsonc
24
+ // feat.config.json
25
+ "services": {
26
+ "model": {
27
+ "adapter": "@mmmnt/feat-adapter-model",
28
+ "consistency": "strong",
29
+ "options": { "dir": ".feature/judge" }
30
+ }
31
+ }
32
+ ```
33
+
34
+ | Option | Type | Meaning |
35
+ | --- | --- | --- |
36
+ | `options.dir` | string | Required. The directory holding the record files, absolute or relative to the project root (the directory holding `feat.config.json`). It is created if missing. |
37
+
38
+ `dir` is the only option. Any other key, a missing `dir` or one that is not a non-empty string is
39
+ `OPTIONS_INVALID` when the adapter is created. Point `dir` at a directory your project ignores (for example under
40
+ `.feature/`).
41
+
42
+ Declare the service `strong` or `acid`. `settle()` vouches as soon as a window is open, so absences are proven
43
+ without waiting out a timeout.
44
+
45
+ ## The record file
46
+
47
+ The file is `<dir>/<namespace>.jsonl`, where the namespace is the one `feat run` derives for each spec file
48
+ (`^s[0-9a-f]{12}$`, SPEC-RT-006). The adapter creates the file empty at `setup()`, empties it at `reset()` (before
49
+ every case) and removes it at `teardown()`. Without a namespace the adapter refuses with `NAMESPACE_INVALID`.
50
+
51
+ Every record is one line: a JSON object, then LF.
52
+
53
+ ```json
54
+ {"type":"judgment.answer","payload":{"purpose":"extraction","unitId":"ws:ws-1/entry:e-42","chunk":0,"candidates":[{"type":"Aggregate","name":"Order","context":"Sales"}]}}
55
+ {"type":"judgment.ask","payload":{"purpose":"extraction","unitId":"ws:ws-1/entry:e-42","chunk":0,"judgmentId":"0123456789abcdef0123456789abcdef"}}
56
+ ```
57
+
58
+ | Field | Required | Meaning |
59
+ | --- | --- | --- |
60
+ | `type` | yes | A non-empty string: the record type the spec names. |
61
+ | `payload` | yes | An object, kept exactly as written. |
62
+ | `key` | no | A string. Without it the record is keyed by its 1-based line number. |
63
+ | `timestamp` | no | A number. Without it the record is stamped with its line number. |
64
+ | `metadata` | no | An object. |
65
+
66
+ A last line without its LF is a write in progress and is not read until its LF lands. A complete line that is not
67
+ such an object fails the case with `RECORD_MALFORMED`, naming the file and the line number.
68
+
69
+ ### What your system does
70
+
71
+ 1. Learn the namespace (from `FEAT_NAMESPACE`, or from your spec host's setup) and the directory, and open
72
+ `<dir>/<namespace>.jsonl`.
73
+ 2. To answer a judgment, read the file's complete lines and find the recorded answer your port's key names (for
74
+ example the hash of the chunk's inputs). Matching an ask to an answer is your port's job: the adapter carries no
75
+ matching rule, so any payload shape works.
76
+ 3. Append each ask as one line, with its LF, before your handler returns. A write buffered past the reply is
77
+ outside the fence and may be missed (ADR-0021).
78
+
79
+ ## Example
80
+
81
+ ```feat
82
+ scenario "the extraction asks once and records the judge's candidates":
83
+ given:
84
+ seed model [ judgment.answer with RecordedJudgeAnswer { purpose: "extraction", unitId: "ws:ws-1/entry:e-42", chunk: 0, candidates: [{ type: "Aggregate", name: "Order", context: "Sales" }] } ]
85
+ when: Extract { pointer: "ws:ws-1/entry:e-42" }
86
+ predict success:
87
+ response OK Extracted { candidates: 1 }
88
+ model has [ judgment.ask with ModelAsk { purpose: "extraction", unitId: "ws:ws-1/entry:e-42", chunk: 0, judgmentId: matching "^[0-9a-f]{32}$" } ]
89
+ ```
90
+
91
+ The seeded answer is in the file before the window opens, so it is never captured. Only the ask the system wrote
92
+ is. `model has []` asserts that the system asked nothing.
93
+
94
+ ## Observation
95
+
96
+ The adapter declares `observes = { diff: "native", state: true }`.
97
+
98
+ | Prediction | What it reads |
99
+ | --- | --- |
100
+ | `model has [...]` | The complete lines appended after `startCapture`, in written order. `peekCapture` reads the same and consumes nothing. With no window open, both return `[]`. |
101
+ | `model contains [...]` | `read()`: every record in the file, seeded answers and asks, in written order. |
102
+
103
+ ## Faults
104
+
105
+ The adapter declares no fault catalog. A `fault model ...` line is `FAULT_UNSUPPORTED` before the case runs. To
106
+ specify a judge that does not answer, seed no answer and predict your system's reaction.
107
+
108
+ ## Errors
109
+
110
+ `ModelAdapterError { code, detail }`:
111
+
112
+ | Code | When |
113
+ | --- | --- |
114
+ | `OPTIONS_INVALID` | At creation: the options are not exactly `{ dir }` with `dir` a non-empty string. The detail names the key. |
115
+ | `NAMESPACE_INVALID` | At creation: no namespace, or one that does not match `^s[0-9a-f]{12}$`. |
116
+ | `RECORD_MALFORMED` | At a capture or a read: a complete line is not JSON, or not an object with a non-empty string `type` and an object `payload`, or its `key`, `timestamp` or `metadata` has the wrong type. The detail names the file and the line number. |
117
+
118
+ ## License
119
+
120
+ MIT
@@ -0,0 +1,13 @@
1
+ import type { FeatServiceAdapter } from "@mmmnt/feat-types";
2
+ export type ModelAdapterErrorCode = "OPTIONS_INVALID" | "NAMESPACE_INVALID" | "RECORD_MALFORMED";
3
+ export declare class ModelAdapterError extends Error {
4
+ readonly code: ModelAdapterErrorCode;
5
+ readonly detail: string;
6
+ constructor(code: ModelAdapterErrorCode, detail: string);
7
+ }
8
+ export interface ModelAdapterOptions {
9
+ /** The directory holding the record files, absolute or relative to the project root. */
10
+ dir: string;
11
+ }
12
+ export declare function createAdapter(config: Record<string, unknown>): FeatServiceAdapter;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAkB,kBAAkB,EAAyB,MAAM,mBAAmB,CAAC;AAEnG,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,kBAAkB,CAAC;AAEjG,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,IAAI,EAAE,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADd,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,MAAM;CAK1B;AAED,MAAM,WAAW,mBAAmB;IAClC,wFAAwF;IACxF,GAAG,EAAE,MAAM,CAAC;CACb;AA6ID,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,kBAAkB,CAEjF"}
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
1
+ // @mmmnt/feat-adapter-model — a recorded judge for a judgment port (SPEC-ADPMODEL-001, ADR-0028).
2
+ //
3
+ // The port's records live in one JSON Lines file per namespace, <dir>/<namespace>.jsonl. The spec seeds the answers
4
+ // the judge gives (the adapter appends them); the system under test reads them from the file and appends its asks.
5
+ // The capture window is the complete lines appended after startCapture. Nothing goes over a network and no model is
6
+ // ever called: this module imports node:fs, node:path and @mmmnt/feat-types only.
7
+ import { appendFileSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import path from "node:path";
9
+ export class ModelAdapterError extends Error {
10
+ code;
11
+ detail;
12
+ constructor(code, detail) {
13
+ super(`${code}: ${detail}`);
14
+ this.code = code;
15
+ this.detail = detail;
16
+ this.name = "ModelAdapterError";
17
+ }
18
+ }
19
+ const NAMESPACE_PATTERN = /^s[0-9a-f]{12}$/;
20
+ function isObject(value) {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22
+ }
23
+ function checkOptions(options) {
24
+ if (!isObject(options))
25
+ throw new ModelAdapterError("OPTIONS_INVALID", "options must be an object { dir }");
26
+ const extra = Object.keys(options).filter((key) => key !== "dir");
27
+ if (extra.length > 0)
28
+ throw new ModelAdapterError("OPTIONS_INVALID", `unknown option(s) ${extra.join(", ")}: the only option is dir`);
29
+ if (typeof options.dir !== "string" || options.dir.length === 0)
30
+ throw new ModelAdapterError("OPTIONS_INVALID", "options.dir must be a non-empty string, the directory holding the record files");
31
+ return { dir: options.dir };
32
+ }
33
+ class ModelAdapter {
34
+ observes = { diff: "native", state: true };
35
+ namespace;
36
+ /** The record file, <dir>/<namespace>.jsonl. */
37
+ file;
38
+ /** Complete lines the file held when the window opened; null with no window open. */
39
+ windowFrom = null;
40
+ constructor(config) {
41
+ const options = checkOptions(config.options);
42
+ const ns = config.namespace;
43
+ if (typeof ns !== "string" || !NAMESPACE_PATTERN.test(ns))
44
+ throw new ModelAdapterError("NAMESPACE_INVALID", `${ns === undefined ? "no namespace" : `'${String(ns)}'`} does not match ${NAMESPACE_PATTERN.source}, the pattern the runner derives`);
45
+ this.namespace = ns;
46
+ const dir = path.resolve(config.projectRoot ?? process.cwd(), options.dir);
47
+ this.file = path.join(dir, `${ns}.jsonl`);
48
+ }
49
+ reportedNamespace() {
50
+ return this.namespace;
51
+ }
52
+ async setup() {
53
+ mkdirSync(path.dirname(this.file), { recursive: true });
54
+ writeFileSync(this.file, "");
55
+ }
56
+ async teardown() {
57
+ this.windowFrom = null;
58
+ rmSync(this.file, { force: true });
59
+ }
60
+ async reset() {
61
+ this.windowFrom = null;
62
+ writeFileSync(this.file, "");
63
+ }
64
+ async seed(records) {
65
+ const lines = records.map((r) => `${JSON.stringify({ type: r.type, payload: r.values })}\n`).join("");
66
+ if (lines.length > 0)
67
+ appendFileSync(this.file, lines);
68
+ }
69
+ /** The complete lines of the file: a last line without its LF is a write in progress. */
70
+ lines() {
71
+ let text;
72
+ try {
73
+ text = readFileSync(this.file, "utf8");
74
+ }
75
+ catch (err) {
76
+ if (err.code === "ENOENT")
77
+ return [];
78
+ throw err;
79
+ }
80
+ const end = text.lastIndexOf("\n");
81
+ return end < 0 ? [] : text.slice(0, end).split("\n");
82
+ }
83
+ record(line, number) {
84
+ const where = `${path.basename(this.file)} line ${number}`;
85
+ let value;
86
+ try {
87
+ value = JSON.parse(line.endsWith("\r") ? line.slice(0, -1) : line);
88
+ }
89
+ catch {
90
+ throw new ModelAdapterError("RECORD_MALFORMED", `${where} (${this.file}) is not JSON`);
91
+ }
92
+ if (!isObject(value) || typeof value.type !== "string" || value.type.length === 0 || !isObject(value.payload))
93
+ throw new ModelAdapterError("RECORD_MALFORMED", `${where} (${this.file}) is not a record { type, payload }`);
94
+ if (value.key !== undefined && typeof value.key !== "string")
95
+ throw new ModelAdapterError("RECORD_MALFORMED", `${where} (${this.file}): key must be a string`);
96
+ if (value.timestamp !== undefined && typeof value.timestamp !== "number")
97
+ throw new ModelAdapterError("RECORD_MALFORMED", `${where} (${this.file}): timestamp must be a number`);
98
+ if (value.metadata !== undefined && !isObject(value.metadata))
99
+ throw new ModelAdapterError("RECORD_MALFORMED", `${where} (${this.file}): metadata must be an object`);
100
+ return {
101
+ type: value.type,
102
+ key: value.key ?? String(number),
103
+ payload: value.payload,
104
+ timestamp: value.timestamp ?? number,
105
+ ...(value.metadata !== undefined ? { metadata: value.metadata } : {}),
106
+ };
107
+ }
108
+ records(from) {
109
+ return this.lines()
110
+ .map((line, i) => ({ line, number: i + 1 }))
111
+ .slice(from)
112
+ .map(({ line, number }) => this.record(line, number));
113
+ }
114
+ async startCapture(_context) {
115
+ this.windowFrom = this.lines().length;
116
+ }
117
+ async stopCapture() {
118
+ const from = this.windowFrom;
119
+ this.windowFrom = null;
120
+ return from === null ? [] : this.records(from);
121
+ }
122
+ async peekCapture() {
123
+ return this.windowFrom === null ? [] : this.records(this.windowFrom);
124
+ }
125
+ /**
126
+ * Every capture reads the file when it is called, so whatever the system has already written (its line complete)
127
+ * is visible to stopCapture: true with a window open, false without one (nothing to vouch for).
128
+ */
129
+ async settle() {
130
+ return this.windowFrom !== null;
131
+ }
132
+ async read(_query) {
133
+ return this.records(0);
134
+ }
135
+ }
136
+ export function createAdapter(config) {
137
+ return new ModelAdapter(config);
138
+ }
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "@mmmnt/feat-adapter-model",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "Recorded-judge service adapter for Feat: a judgment port's seeded answers and captured asks in one JSON Lines file per namespace; no network, never calls a model",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "dependencies": {
12
+ "@mmmnt/feat-types": "^0.4.0"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.8.0",
16
+ "vitest": "^3.2.0",
17
+ "@mmmnt/feat-adapter-kit": "^0.1.7",
18
+ "@mmmnt/feat-runtime": "^0.4.0"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/mmmnt/feature.git",
27
+ "directory": "adapters/model"
28
+ },
29
+ "homepage": "https://github.com/mmmnt/feature#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/mmmnt/feature/issues"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "build": "tsc -p tsconfig.json",
38
+ "typecheck": "tsc -p tsconfig.json --noEmit",
39
+ "test": "vitest run --exclude '**/observe.test.ts' --passWithNoTests"
40
+ }
6
41
  }