create-t2k 0.4.0 → 0.4.1
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/README.md +26 -9
- package/package.json +1 -1
- package/template/README.md.template +16 -4
- package/template/compose.yml +3 -1
- package/template/package.json.template +1 -1
- package/template/src/lifecycle.mjs +15 -2
- package/template/src/run.mjs +15 -2
- package/template-integration-hub/README.md.template +29 -15
- package/template-integration-hub/package.json.template +1 -1
- package/template-integration-hub/src/run.mjs +44 -28
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ a Decision Context, two executable policies, and disjoint replay evidence.
|
|
|
5
5
|
The existing decision-loop project remains the default profile.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx create-t2k@
|
|
8
|
+
npx create-t2k@0.4.1 my-decision-loop
|
|
9
9
|
cd my-decision-loop
|
|
10
10
|
npm start
|
|
11
11
|
```
|
|
@@ -31,6 +31,10 @@ Stop the local containers without deleting lifecycle data with `npm run
|
|
|
31
31
|
db:down`. Use the explicitly destructive `npm run db:reset` only when you
|
|
32
32
|
intend to delete the disposable local database volume.
|
|
33
33
|
|
|
34
|
+
Compose binds PostgreSQL to `127.0.0.1:55432` only. The included `t2k` username
|
|
35
|
+
and `t2k` password are disposable local-only quickstart credentials; never
|
|
36
|
+
reuse them or expose this generated database to another host.
|
|
37
|
+
|
|
34
38
|
To expose ontology validation, compilation, policy execution, replay, and
|
|
35
39
|
reward evaluation to an MCP host, add:
|
|
36
40
|
|
|
@@ -39,7 +43,7 @@ reward evaluation to an MCP host, add:
|
|
|
39
43
|
"mcpServers": {
|
|
40
44
|
"t2k": {
|
|
41
45
|
"command": "npx",
|
|
42
|
-
"args": ["-y", "@t2kai/mcp@
|
|
46
|
+
"args": ["-y", "@t2kai/mcp@0.3.0"]
|
|
43
47
|
}
|
|
44
48
|
}
|
|
45
49
|
}
|
|
@@ -52,9 +56,12 @@ before enabling database access or agent writes.
|
|
|
52
56
|
Use `--no-install` to generate files without running `npm install`:
|
|
53
57
|
|
|
54
58
|
```bash
|
|
55
|
-
npx create-t2k@
|
|
59
|
+
npx create-t2k@0.4.1 my-decision-loop --no-install
|
|
56
60
|
```
|
|
57
61
|
|
|
62
|
+
The normal command installs dependencies for you. Run `npm install` inside the
|
|
63
|
+
generated project only when you chose `--no-install`.
|
|
64
|
+
|
|
58
65
|
The command refuses to write into a non-empty directory. Node.js 20.10 or newer
|
|
59
66
|
is required.
|
|
60
67
|
|
|
@@ -63,7 +70,7 @@ is required.
|
|
|
63
70
|
Generate a focused, fully synthetic source-integration project explicitly:
|
|
64
71
|
|
|
65
72
|
```bash
|
|
66
|
-
npx create-t2k@
|
|
73
|
+
npx create-t2k@0.4.1 my-integration-hub --profile integration-hub
|
|
67
74
|
cd my-integration-hub
|
|
68
75
|
npm start
|
|
69
76
|
```
|
|
@@ -71,21 +78,31 @@ npm start
|
|
|
71
78
|
This profile maps two independent registry records into one canonical party
|
|
72
79
|
identity, preserves conflicting name evidence, and applies a versioned authority
|
|
73
80
|
order to a conflicting registry state. It runs reconciliation again with the
|
|
74
|
-
opposite input order and
|
|
81
|
+
opposite input order and reports that specifically scoped proposal-hash
|
|
82
|
+
comparison; it does not claim to check every permutation after more sources are
|
|
83
|
+
added.
|
|
75
84
|
|
|
76
85
|
Every regular `source-records/*.json` file is discovered in canonical filename
|
|
77
86
|
order, so the generated README experiments work without editing the runner:
|
|
78
|
-
reverse the
|
|
79
|
-
the same canonical key and run `npm start` again.
|
|
87
|
+
reverse the authority priorities while bumping `policyVersion`, or add a third
|
|
88
|
+
synthetic source with the same canonical key and run `npm start` again.
|
|
80
89
|
|
|
81
90
|
The output is a deterministic evidence packet for human review. It preserves
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
every canonical record with its complete receipt, the exact loaded
|
|
92
|
+
`@t2kai/core` package version, the complete authority policy, and the full
|
|
93
|
+
forward and reverse reconciliation proposals and issues inside the hashed
|
|
94
|
+
packet. It does not mutate any source record or promote the authority-selected
|
|
84
95
|
candidate to accepted truth. Both included synthetic sources deliberately have
|
|
85
96
|
an `unknown` authentication state. The unkeyed hashes demonstrate deterministic
|
|
86
97
|
self-consistency only; they are not signatures, authentication, or proof that a
|
|
87
98
|
source assertion is true.
|
|
88
99
|
|
|
100
|
+
After an envelope has been mapped, keep it immutable: represent a later
|
|
101
|
+
observation with a new file, source-record key, payload record ID, and event and
|
|
102
|
+
observation times. Bump `policyVersion` after changing authority priorities;
|
|
103
|
+
bump `mappingVersion` and `ontologyVersion` after changing a mapping; and bump
|
|
104
|
+
`ontologyVersion` for any other ontology contract change.
|
|
105
|
+
|
|
89
106
|
Supported profiles are `decision-loop` (the default) and `integration-hub`.
|
|
90
107
|
An unknown or repeated `--profile` option fails before the target directory is
|
|
91
108
|
created.
|
package/package.json
CHANGED
|
@@ -4,10 +4,12 @@ This project is a local T2K governed-decision quickstart. Its data is fully
|
|
|
4
4
|
synthetic.
|
|
5
5
|
|
|
6
6
|
```bash
|
|
7
|
-
npm install
|
|
8
7
|
npm start
|
|
9
8
|
```
|
|
10
9
|
|
|
10
|
+
`create-t2k` installs dependencies by default. Run `npm install` first only if
|
|
11
|
+
this project was generated with `--no-install`.
|
|
12
|
+
|
|
11
13
|
The run performs five explicit steps:
|
|
12
14
|
|
|
13
15
|
1. validate `ontology-pack.json` against the published T2K schema;
|
|
@@ -44,6 +46,10 @@ the local containers while preserving their volume, run `npm run db:down`. To
|
|
|
44
46
|
explicitly delete the disposable local volume and all lifecycle data in it, run
|
|
45
47
|
`npm run db:reset`.
|
|
46
48
|
|
|
49
|
+
Compose binds PostgreSQL to `127.0.0.1:55432` only. Its `t2k` username and
|
|
50
|
+
`t2k` password are disposable local-only quickstart credentials; never reuse
|
|
51
|
+
them outside this generated project or expose this database to another host.
|
|
52
|
+
|
|
47
53
|
## Connect an MCP host
|
|
48
54
|
|
|
49
55
|
Expose validation, compilation, policy execution, replay, and reward evaluation
|
|
@@ -54,7 +60,7 @@ to an MCP host with the safe database-free mode:
|
|
|
54
60
|
"mcpServers": {
|
|
55
61
|
"t2k": {
|
|
56
62
|
"command": "npx",
|
|
57
|
-
"args": ["-y", "@t2kai/mcp@
|
|
63
|
+
"args": ["-y", "@t2kai/mcp@0.3.0"]
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
}
|
|
@@ -67,9 +73,15 @@ mutation modes.
|
|
|
67
73
|
## Change the example
|
|
68
74
|
|
|
69
75
|
- Change current facts in `decision-context.json`.
|
|
70
|
-
- Change executable rules in `policies/*.json`.
|
|
76
|
+
- Change executable rules in `policies/*.json` and bump the policy `version`.
|
|
71
77
|
- Add observed outcomes to `episodes/holdout.json` without reusing training data.
|
|
72
|
-
- Change concepts
|
|
78
|
+
- Change concepts or decision contracts in `ontology-pack.json` and bump
|
|
79
|
+
`ontologyVersion`.
|
|
80
|
+
|
|
81
|
+
Treat any context, policy, episode, or ontology artifact that has already been
|
|
82
|
+
executed or persisted as immutable evidence. Model a later observation with a
|
|
83
|
+
new context or episode identifier, and keep the prior artifact available for
|
|
84
|
+
replay instead of rewriting history.
|
|
73
85
|
|
|
74
86
|
The quickstart uses local files, local PostgreSQL, and `@t2kai/core`; it does not
|
|
75
87
|
send data to a hosted service. Set `T2K_DATABASE_URL` to use another Postgres
|
package/template/compose.yml
CHANGED
|
@@ -2,6 +2,8 @@ services:
|
|
|
2
2
|
postgres:
|
|
3
3
|
image: postgres:16-alpine
|
|
4
4
|
environment:
|
|
5
|
+
# Disposable local-only quickstart credentials. Never reuse t2k/t2k
|
|
6
|
+
# outside this generated project.
|
|
5
7
|
POSTGRES_DB: t2k_reference
|
|
6
8
|
POSTGRES_PASSWORD: t2k
|
|
7
9
|
POSTGRES_USER: t2k
|
|
@@ -11,7 +13,7 @@ services:
|
|
|
11
13
|
timeout: 5s
|
|
12
14
|
retries: 15
|
|
13
15
|
ports:
|
|
14
|
-
- "55432:5432"
|
|
16
|
+
- "127.0.0.1:55432:5432"
|
|
15
17
|
volumes:
|
|
16
18
|
- t2k-reference-data:/var/lib/postgresql/data
|
|
17
19
|
|
|
@@ -10,8 +10,21 @@ import { compileOntologyPackSet } from "@t2kai/core/compiler";
|
|
|
10
10
|
import { PostgresReferenceLifecycle } from "@t2kai/core/postgres";
|
|
11
11
|
|
|
12
12
|
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
async function readJson(relativePath) {
|
|
15
|
+
const contents = await fs.readFile(
|
|
16
|
+
path.join(projectRoot, relativePath),
|
|
17
|
+
"utf8"
|
|
18
|
+
);
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(contents);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
23
|
+
throw new SyntaxError(`Invalid JSON in ${relativePath}${detail}`, {
|
|
24
|
+
cause: error,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
15
28
|
const [manifest, baseline, candidatePolicy, holdout] = await Promise.all([
|
|
16
29
|
readJson("ontology-pack.json"),
|
|
17
30
|
readJson("policies/baseline.json"),
|
package/template/src/run.mjs
CHANGED
|
@@ -12,8 +12,21 @@ import {
|
|
|
12
12
|
import { compileOntologyPackSet } from "@t2kai/core/compiler";
|
|
13
13
|
|
|
14
14
|
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
|
|
16
|
+
async function readJson(relativePath) {
|
|
17
|
+
const contents = await fs.readFile(
|
|
18
|
+
path.join(projectRoot, relativePath),
|
|
19
|
+
"utf8"
|
|
20
|
+
);
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(contents);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
25
|
+
throw new SyntaxError(`Invalid JSON in ${relativePath}${detail}`, {
|
|
26
|
+
cause: error,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
17
30
|
|
|
18
31
|
const [manifest, context, baseline, candidate, episodes] = await Promise.all([
|
|
19
32
|
readJson("ontology-pack.json"),
|
|
@@ -5,10 +5,12 @@ two independently governed source records can be mapped into one canonical
|
|
|
5
5
|
identity without silently merging evidence or declaring either source true.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install
|
|
9
8
|
npm start
|
|
10
9
|
```
|
|
11
10
|
|
|
11
|
+
`create-t2k` installs dependencies by default. Run `npm install` first only if
|
|
12
|
+
this project was generated with `--no-install`.
|
|
13
|
+
|
|
12
14
|
The run performs these explicit steps:
|
|
13
15
|
|
|
14
16
|
1. validate and compile `ontology-pack.json`;
|
|
@@ -17,8 +19,9 @@ The run performs these explicit steps:
|
|
|
17
19
|
3. preserve all conflicting display-name candidates;
|
|
18
20
|
4. apply `authority-policy.json` to select a registry-state candidate within
|
|
19
21
|
the proposal;
|
|
20
|
-
5.
|
|
21
|
-
|
|
22
|
+
5. compare the forward source order with its reverse (not every possible
|
|
23
|
+
ordering when more than two sources exist); and
|
|
24
|
+
6. emit a repeatable evidence packet for human review.
|
|
22
25
|
|
|
23
26
|
The selected registry-state value is only a candidate within a non-mutating
|
|
24
27
|
proposal. No source record is changed and no value becomes accepted truth.
|
|
@@ -26,18 +29,29 @@ Both source envelopes deliberately use `authenticationState: "unknown"`.
|
|
|
26
29
|
Production callers must authenticate or attest sources and policies outside
|
|
27
30
|
this example.
|
|
28
31
|
|
|
29
|
-
Each complete source-mapping receipt
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
Each canonical record is paired with its complete source-mapping receipt in the
|
|
33
|
+
evidence packet. The packet also includes the exact loaded `@t2kai/core`
|
|
34
|
+
package version, the complete authority policy, both complete reconciliation
|
|
35
|
+
proposals and their issues, and an explicitly scoped forward-versus-reverse
|
|
36
|
+
hash comparison. All of those fields are included before the packet hash is
|
|
37
|
+
computed. The receipt, policy, proposal, and packet hashes are unkeyed
|
|
38
|
+
deterministic self-consistency checks. They are not signatures, authentication
|
|
39
|
+
tokens, or proof that an assertion is true.
|
|
40
|
+
|
|
41
|
+
## Explore the hub safely
|
|
42
|
+
|
|
43
|
+
- Treat a source envelope as immutable once it has been mapped. To model a
|
|
44
|
+
later observation, copy it to a new `.json` file and assign a new
|
|
45
|
+
`sourceRecordKey`, payload `record_id`, `eventTime`, and `observedTime`; never
|
|
46
|
+
rewrite the earlier envelope.
|
|
47
|
+
- To reverse the priorities in `authority-policy.json`, also bump
|
|
48
|
+
`policyVersion`, rerun, and inspect the changed proposed authority selection.
|
|
49
|
+
- Add a third synthetic `.json` source with the same `party_key` and its own
|
|
50
|
+
new envelope identifiers and timestamps; it is discovered automatically on
|
|
51
|
+
the next run.
|
|
52
|
+
- When a field mapping changes, bump its `mappingVersion`. Because mappings are
|
|
53
|
+
part of this ontology pack, also bump `ontologyVersion`; bump
|
|
54
|
+
`ontologyVersion` for any other ontology contract change as well.
|
|
41
55
|
- Replace `humanCheckpoint: "always"` only after defining a real review and
|
|
42
56
|
authorization boundary in the calling system.
|
|
43
57
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
@@ -15,9 +16,18 @@ import {
|
|
|
15
16
|
} from "@t2kai/core/compiler";
|
|
16
17
|
|
|
17
18
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
19
|
+
const loadPackageManifest = createRequire(import.meta.url);
|
|
18
20
|
|
|
19
21
|
async function readJson(relativePath) {
|
|
20
|
-
|
|
22
|
+
const contents = await fs.readFile(path.join(root, relativePath), "utf8");
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(contents);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
const detail = error instanceof Error ? `: ${error.message}` : "";
|
|
27
|
+
throw new SyntaxError(`Invalid JSON in ${relativePath}${detail}`, {
|
|
28
|
+
cause: error,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
21
31
|
}
|
|
22
32
|
|
|
23
33
|
async function readSourceRecords() {
|
|
@@ -43,6 +53,17 @@ const manifest = await readJson("ontology-pack.json");
|
|
|
43
53
|
const authorityPolicy = await readJson("authority-policy.json");
|
|
44
54
|
const sourceRecords = await readSourceRecords();
|
|
45
55
|
const sourceSnapshot = structuredClone(sourceRecords);
|
|
56
|
+
const corePackageManifest = loadPackageManifest("@t2kai/core/package.json");
|
|
57
|
+
assert.equal(
|
|
58
|
+
corePackageManifest.name,
|
|
59
|
+
"@t2kai/core",
|
|
60
|
+
"The integration packet must identify the loaded core runtime."
|
|
61
|
+
);
|
|
62
|
+
assert.ok(
|
|
63
|
+
typeof corePackageManifest.version === "string" &&
|
|
64
|
+
corePackageManifest.version.length > 0,
|
|
65
|
+
"The loaded core runtime must expose an exact package version."
|
|
66
|
+
);
|
|
46
67
|
|
|
47
68
|
const validation = validateOntologyPackManifest(manifest);
|
|
48
69
|
assert.equal(
|
|
@@ -160,8 +181,13 @@ assert.ok(preferAuthorityField.candidates.length >= 2);
|
|
|
160
181
|
|
|
161
182
|
const packetWithoutHash = {
|
|
162
183
|
profile: "integration-hub",
|
|
184
|
+
coreRuntime: {
|
|
185
|
+
packageName: corePackageManifest.name,
|
|
186
|
+
packageVersion: corePackageManifest.version,
|
|
187
|
+
},
|
|
163
188
|
ontology: `${manifest.ontologyId}@${manifest.ontologyVersion}`,
|
|
164
189
|
resolutionHash: compilation.resolutionHash,
|
|
190
|
+
authorityPolicy: structuredClone(authorityPolicy),
|
|
165
191
|
sourceEvidence: mappedResults
|
|
166
192
|
.map((result) => ({
|
|
167
193
|
summary: {
|
|
@@ -172,6 +198,7 @@ const packetWithoutHash = {
|
|
|
172
198
|
receiptHash: result.receipt.receiptHash,
|
|
173
199
|
humanReviewRequired: result.receipt.humanReviewRequired,
|
|
174
200
|
},
|
|
201
|
+
canonicalRecord: result.canonicalRecord,
|
|
175
202
|
receipt: result.receipt,
|
|
176
203
|
}))
|
|
177
204
|
.sort((left, right) =>
|
|
@@ -181,38 +208,27 @@ const packetWithoutHash = {
|
|
|
181
208
|
)
|
|
182
209
|
),
|
|
183
210
|
reconciliation: {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
resolution: preserveAllField.resolution,
|
|
200
|
-
selectedValue: preserveAllField.selectedValue,
|
|
201
|
-
candidates: preserveAllField.candidates,
|
|
202
|
-
},
|
|
203
|
-
preferAuthority: {
|
|
204
|
-
propertyRef: preferAuthorityField.propertyRef,
|
|
205
|
-
resolution: preferAuthorityField.resolution,
|
|
206
|
-
selectedWithinProposal: preferAuthorityField.selectedValue,
|
|
207
|
-
candidates: preferAuthorityField.candidates,
|
|
211
|
+
proposal: reconciliation,
|
|
212
|
+
reverseInputOrderProposal: reverseOrderReconciliation,
|
|
213
|
+
forwardReverseInputOrderCheck: {
|
|
214
|
+
comparisonScope: "forward_and_reverse_input_order_only",
|
|
215
|
+
allPermutationsChecked: mappedResults.length === 2,
|
|
216
|
+
forwardReceiptHashes: mappedResults.map(
|
|
217
|
+
(result) => result.receipt.receiptHash
|
|
218
|
+
),
|
|
219
|
+
reverseReceiptHashes: [...mappedResults]
|
|
220
|
+
.reverse()
|
|
221
|
+
.map((result) => result.receipt.receiptHash),
|
|
222
|
+
forwardProposalHash: reconciliation.proposalHash,
|
|
223
|
+
reverseProposalHash: reverseOrderReconciliation.proposalHash,
|
|
224
|
+
proposalHashesMatch:
|
|
225
|
+
reconciliation.proposalHash === reverseOrderReconciliation.proposalHash,
|
|
208
226
|
},
|
|
209
227
|
},
|
|
210
228
|
humanReview: {
|
|
211
229
|
status: "pending_human_review",
|
|
212
230
|
proposalOnly: true,
|
|
213
|
-
issues: reconciliation.issues
|
|
214
|
-
.filter((issue) => issue.severity === "review")
|
|
215
|
-
.map(({ code, message, receiptHash }) => ({ code, message, receiptHash })),
|
|
231
|
+
issues: reconciliation.issues,
|
|
216
232
|
},
|
|
217
233
|
boundaries: {
|
|
218
234
|
syntheticDataOnly: true,
|