@nanobpm/nano-workforce 0.170.1 → 0.171.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/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [0.171.1](https://github.com/nanobpm/nano-workforce/compare/v0.171.0...v0.171.1) (2026-08-31)
2
+
3
+ ### Code Refactoring
4
+
5
+ * **agentic:** consume @nanobpm/agentic/transcript, drop the duplicated grammar ([#677](https://github.com/nanobpm/nano-workforce/issues/677)) ([8c8d3fc](https://github.com/nanobpm/nano-workforce/commit/8c8d3fc393134862557a65e2aab2dd9e16292c66)), closes [#676](https://github.com/nanobpm/nano-workforce/issues/676)
6
+
7
+ ## [0.171.0](https://github.com/nanobpm/nano-workforce/compare/v0.170.1...v0.171.0) (2026-08-31)
8
+
9
+ ### Features
10
+
11
+ * **operations:** app-owned POST /actions/cancel door (cancelInstance) ([#671](https://github.com/nanobpm/nano-workforce/issues/671)) ([835afce](https://github.com/nanobpm/nano-workforce/commit/835afcea6be62fc768979b792217ad5cf246d008)), closes [#605](https://github.com/nanobpm/nano-workforce/issues/605) [#667](https://github.com/nanobpm/nano-workforce/issues/667)
12
+
1
13
  ## [0.170.1](https://github.com/nanobpm/nano-workforce/compare/v0.170.0...v0.170.1) (2026-08-31)
2
14
 
3
15
  ### Documentation
@@ -1,11 +1,12 @@
1
- // Drift-guard: exactly ONE parser of the transcript log (ADR 0056, #251).
1
+ // Drift-guard: the transcript grammar is DERIVED from its one owner, never re-forked here (#676).
2
2
  //
3
- // Acceptance criterion (#251): "the cockpit renders from a single derive*() fold, with no independent
4
- // re-parse of raw bytes (drift-guard test asserts one parser)". This is that guard. It enforces
5
- // structurallyby scanning the app-tier source that the raw-chunk typed-event classification
6
- // lives in exactly one module (`transcript-events.ts`), so a second, divergent parser of the same
7
- // bytes cannot creep in. The whole point of the event-sourced model is "the log IS the state": every
8
- // view derives from the one fold, none re-parses the bytes itself.
3
+ // nano-workforce used to carry a byte-for-byte hand-rolled copy of the transcript-event grammar
4
+ // (marker/version, the ONE parser, the vocabulary, the derive fold). That grammar now lives in exactly
5
+ // one place `@nanobpm/agentic/transcript` (agentic 0.10.0)and `transcript-events.ts` is a thin
6
+ // re-export barrel over it. This guard secures the *class* of failure ("a consumer hand-rolls the
7
+ // transcript grammar instead of importing it") structurally, by scanning the app-tier source: it fails
8
+ // if the barrel stops importing agentic, if a local module re-defines the envelope marker literal, or
9
+ // if a transcript consumer re-parses a stored chunk itself instead of folding through the one parser.
9
10
  import { test } from "node:test";
10
11
  import { readdirSync, readFileSync } from "node:fs";
11
12
  import { dirname, join } from "node:path";
@@ -26,26 +27,36 @@ function sourceFiles(dir: string): string[] {
26
27
  return out;
27
28
  }
28
29
 
29
- const PARSER_MODULE = join(AGENTIC_DIR, "transcript-events.ts");
30
+ const BARREL_MODULE = join(AGENTIC_DIR, "transcript-events.ts");
31
+ const AGENTIC_TRANSCRIPT_SPECIFIER = "@nanobpm/agentic/transcript";
30
32
 
31
- test("the transcript-event marker literal is DEFINED in exactly one module (no second parser)", () => {
32
- // Consumers reference the marker via the imported `TRANSCRIPT_EVENT_MARKER` identifier; only the ONE
33
- // parser embeds the marker's string literal. A second module hardcoding it would be a second parser.
34
- // Match every quote form (double, single, backtick) so a second parser can't bypass the guard by
35
- // hardcoding the marker in a different literal style.
33
+ test("the transcript grammar is imported from @nanobpm/agentic/transcript, never redefined locally", () => {
34
+ // The single source of truth is agentic; nano-workforce derives from it via a re-export barrel.
35
+ const barrel = readFileSync(BARREL_MODULE, "utf8");
36
+ assert(
37
+ barrel.includes(AGENTIC_TRANSCRIPT_SPECIFIER),
38
+ `${BARREL_MODULE} must re-export the grammar from "${AGENTIC_TRANSCRIPT_SPECIFIER}"`,
39
+ );
40
+ });
41
+
42
+ test("no local module re-defines the transcript-event marker literal (no second grammar)", () => {
43
+ // Consumers reference the marker via the imported `TRANSCRIPT_EVENT_MARKER` identifier; only a
44
+ // second, forked grammar would embed the marker's string literal in nano-workforce source. Match
45
+ // every quote form (double, single, backtick) so a re-fork can't bypass the guard by hardcoding the
46
+ // marker in a different literal style. Expect ZERO owners — the literal lives in agentic now.
36
47
  const quotedMarkerForms = ['"', "'", "`"].map((q) => `${q}${TRANSCRIPT_EVENT_MARKER}${q}`);
37
48
  const owners = sourceFiles(AGENTIC_DIR).filter((path) => {
38
49
  const src = readFileSync(path, "utf8");
39
50
  return quotedMarkerForms.some((literal) => src.includes(literal));
40
51
  });
41
- assertEquals(owners, [PARSER_MODULE]);
52
+ assertEquals(owners, []);
42
53
  });
43
54
 
44
- test("no transcript consumer re-parses raw chunks — JSON.parse of the log lives only in the parser", () => {
45
- // The cockpit + read projections must fold through the single parser, never JSON.parse a chunk
46
- // themselves. Scan the transcript-facing consumers and assert none contains a raw JSON.parse.
47
- const consumers = sourceFiles(AGENTIC_DIR).filter(
48
- (path) => path !== PARSER_MODULE && /transcript-(read|render|view|derive|fork)\.ts$/.test(path),
55
+ test("no transcript consumer re-parses raw chunks — the log is folded only through the one parser", () => {
56
+ // The cockpit + read projections must fold through the single parser (in agentic), never JSON.parse a
57
+ // chunk themselves. Scan the transcript-facing consumers and assert none contains a raw JSON.parse.
58
+ const consumers = sourceFiles(AGENTIC_DIR).filter((path) =>
59
+ /transcript-(read|render|view|derive|fork)\.ts$/.test(path),
49
60
  );
50
61
  assert(consumers.length >= 3, "expected to scan several transcript consumers");
51
62
  for (const path of consumers) {