← Back to Articles & Artefacts
artefactseast

Opus Review 769

IAIP Research
ep76-proposal-scoring-with-jev-261010-261010-0d54154e-d8cd-4c8e-8eb5-2368d3a1032b

🧠: Review of jgwill/Miadi 71387fae^..4ace34b8 (six commits, all #769)

Model class: Opus (claude-opus-5-5). Read-only review at 4ace34b8.

Scope and method#

Commits read: 71387fae (notebooks), f69a6a17 (prototypes), 43d99302 (tracing in the manifest), 45d5ad25 (@miadi/tracing over @miadi/tracing-langfuse), 82a282dd (addresses and doors), 4ace34b8 (sessions). 67 files, +7034/-46.

Probes run, all read-only:

  • The new parsers on built dist/ with node --input-type=module -e (no files written).
  • esbuild --platform=browser --outfile=/dev/null on @miadi/reference-ui and each @miadi/inquiry-weave/references module.
  • The schema readers over the 150 real episode.yaml files in the chronicle (read, never written), plus git config --get remote.origin.url there.
  • Field shapes of /src/_sessiondata/data/terminal_bindings.jsonl (942 lines). Only the shapes were extracted, no argv or cwd values.
  • @langfuse/client 5.13.1 type definitions.
  • The medicine-wheel PATCH /api/nodes/[id] handler.
  • tsc --noEmit -p tsconfig.json --incremental false on the app.

Writes beyond this report: npm test rebuilt each package's gitignored dist/ (git status is clean for the five packages afterwards). Test logs and the tsc log are in this scratchpad (tests/*.log, tsc-app.log).

Verified as holding#

  • The editing address (editor_url) and the provider page (ui_url) are never written to episode.yaml or to a commit body. They live on the wheel node. GET .../prototypes and GET .../tracing add them only when mayWrite(request) is true.
  • mayWrite has no loopback bypass for write (lib/api-gate.ts:297). It needs a writer token or a person with the write tier.
  • No app route proxies wheel node metadata to readers. Only artefactRef.ts reads nodes, and it returns name and episode_path only.
  • The wheel's PATCH merges metadata (medicine-wheel app/api/nodes/[id]/route.ts:74-98), so the editor_url, ui_url and commit PATCHes do not wipe other node metadata.
  • The schema modules import no fs and no network.
  • App type-check: 69 error lines, none in a file these commits touch.
  • Sessions: the real binding line has one timestamp format and episode as {id, source} with the full folder name. On real data, no session ends up shown as live after it ended.

Test results (verbatim)#

episodic-memory-schema  ℹ tests 167  ℹ pass 167  ℹ fail 0   EXIT 0
tracing                 ℹ tests 3    ℹ pass 3    ℹ fail 0   EXIT 0
tracing-langfuse        ℹ tests 3    ℹ pass 3    ℹ fail 0   EXIT 0
inquiry-weave           ℹ tests 260  ℹ pass 260  ℹ fail 0   EXIT 0
reference-ui            ℹ tests 19   ℹ pass 19   ℹ fail 0   EXIT 0

All pass. F1, F2, F3 and F5 below pass these suites.

Findings#

F1. @miadi/inquiry-weave/references and @miadi/reference-ui no longer bundle for a browser#

  • Where: packages/inquiry-weave/src/rise-dsl.ts:21-22, references.ts:12, text-references.ts:39.
  • Defect: rise-dsl.ts imports node:fs and node:path, and references.ts re-exports it. text-references.ts imports it too, and markdown-directives.ts imports text-references.ts. So the whole ./references subpath, which was browser-clean before 82a282dd, now needs Node.
  • Scenario: esbuild packages/reference-ui/dist/index.js --bundle --platform=browser fails with Could not resolve "node:fs" at inquiry-weave/dist/rise-dsl.js:20.
    • Per module: chronicle-dsl ok, foundation-dsl ok, artefact-dsl ok, text-references FAIL, markdown-directives FAIL, rise-dsl FAIL.
    • In the app, components/ceremony/turn-text.tsx:17-19 is a "use client" component that imports both packages. Next 16's webpack client config falls back for process only (next/dist/build/webpack-config.js:608-610).
    • No Next build has compiled these commits yet. .next/BUILD_ID is dated 14:59 and 82a282dd landed at 21:25. Turbopack is untested here.
  • Severity: blocks publish (@miadi/reference-ui 0.2.0 is sold as "any host", and inquiry-weave 0.17.0 ./references is what it uses).
  • Smallest fix: move KNOWN_RISE_WORKSPACES, riseWorkspacePath, riseLocalPath and the two node imports out of rise-dsl.ts into a server-only module exported from the package root. They have no caller anyway (F21). Then rerun the esbuild line above.

F2. A versioned prompt address never resolves#

  • Where: packages/inquiry-weave/src/artefact-dsl.ts:101, :133-135. Consumed at app/chronicle/lib/artefactRef.ts:59, :95.
  • Defect: parseArtefactUri keeps @<version> in id, and artefactNodeId builds tracing:<domain>/prompt/<name>@<v>. The door writes the node as tracing:<domain>/prompt/<name>, with no version (tracing.ts:250 via tracingNodeId).
  • Scenario (proven): miadi-prompt:ceremony/miadi-ep120-fork@2 gives artefactNodeId = tracing:ceremony/prompt/miadi-ep120-fork@2, but the door's node id is tracing:ceremony/prompt/miadi-ep120-fork. The wheel answers 404, the chip renders unresolved, and /api/chronicle/open answers 404 "Artefact not found".
  • Severity: fix before publish.
  • Smallest fix: for kind === "tracing", artefactNodeId returns tracingNodeId(parsed.tracing.domain, parsed.tracing.resource, parsed.tracing.id). The version stays for display.

F3. The Langfuse provider summarises a trace under its root span's name, not the trace name#

  • Where: packages/tracing-langfuse/src/index.ts:106, :92.
  • Defect: fields: "core,basic" does not include traceName. In SDK 5.13.1 that field is in the trace_context group (@langfuse/core/dist/index.d.ts:10591: "trace_context - tags, release, traceName"). So :92 always falls back to root.name.
  • Scenario: a trace named "Miadi Episode 120 | branching" through updateTrace, whose root span is "handle-turn", is described as name: "handle-turn".
  • Severity: fix before publish.
  • Smallest fix: OBSERVATION_FIELDS = "core,basic,trace_context". This also brings tags for F4.

F4. listTraces filters by observation name, and ignores tags#

  • Where: packages/tracing-langfuse/src/index.ts:196, :205, :204.
  • Defect: request.name filters observations by their own name, not by trace name. const wanted = options.tags?.length ? traces : traces is a dead branch, so tags are silently ignored. Each summary is built from whichever observations the one page returned.
  • Scenario:
    • listTraces("ceremony", { tags: ["ep120"] }) returns the newest traces whatever their tags.
    • listTraces("ceremony", { name: "Miadi Episode 120 | branching" }) returns nothing unless a span carries that name.
    • The README says the package answers tags. It never does.
  • Severity: fix before publish (or remove the options from the contract).
  • Smallest fix: pass the structured filter with the traceName and tags columns the SDK documents, or drop name and tags from TracingProvider.listTraces until they work.

F5. The manifest reader ignores the miadi-trace: and miadi-prompt: aliases in references:#

  • Where: packages/episodic-memory-schema/src/episode-tracing.ts:120-127, :237-238.
  • Defect: parseTracingRef strips only miadi-tracing: and tracing:. artefact-dsl.ts:96 accepts both aliases.
  • Scenario (proven): readEpisodeTracing({references: ["miadi-prompt:ceremony/foo@2", "miadi-trace:eng/abc123"]}) returns []. The same strings in prose render as tracing chips, but the room's "Traces and prompts" card does not list them.
  • Severity: fix before publish (the schema package is where the aliases need to be read).
  • Smallest fix: rewrite the two aliases to a key in parseTracingRef the way artefact-dsl.ts:96 does.

F6. Resolve and open fail open when the audience lookup throws#

  • Where: app/api/chronicle/resolve/route.ts:84-89, app/api/chronicle/open/route.ts:86-92, app/chronicle/lib/artefactRef.ts:61, :98.
  • Defect: the catch sets visible = null, and artefactRef reads null as "no restriction". In the same route, the chronicle path fails closed (resolve/route.ts:105-107 leaves entries empty), and references.tsx:236 uses an empty Set.
  • Scenario: getVisibleManifestFor throws (for example, buildChronicleCatalog cannot read MIADI_CHRONICLE_ROOT) while the wheel answers. An anonymous GET /api/chronicle/resolve?uri=miadi-notebook:<uuid held by a community episode> then returns the community episode's name, number and the notebook title, and /open redirects.
  • Severity: fix before publish (app code that ships with the :3335 rebuild).
  • Smallest fix: visible = new Set() in both catches.

F7. The "pure, host-neutral" schema exports gaia's checkout paths#

  • Where: packages/episodic-memory-schema/src/episode-rispec.ts:64-70, :96-104, exported at index.ts:244.
  • Defect: HOST_PATH_REPOSITORIES hard-codes /src/Miadi/, /a/src/Miadi/, /a/src/Miadi-18/, /etc/claude-code/ and /srv/miadi/episodes/. parseRiseRef applies it on every host. Once published, it is public API.
  • Scenario: on ilex or any host with other paths, a manifest host path is not read. On every host, /a/src/Miadi-18/x is read as jgwill/Miadi, whatever that path holds there.
  • Severity: fix before publish (removing it after publish is a breaking change).
  • Smallest fix: parseRiseRef(input, { hostPaths = [] }) in the schema. inquiry-weave, the host layer, passes the map.

F8. @miadi/tracing caches a failed project lookup forever#

  • Where: packages/tracing/src/index.ts:165-173.
  • Defect: provider.project(domain).catch(() => null) is stored in the map, so one failure is cached for the life of the process.
  • Scenario: Langfuse answers 503 on the first uiUrl call. Every later uiUrl for that domain returns null until the process restarts.
  • Severity: fix before publish (one line).
  • Smallest fix: cache only a resolved non-null project, and delete the map entry on failure or null.

F9. A trace summary reads one page of at most 100 observations#

  • Where: packages/tracing-langfuse/src/index.ts:186, :95, :102.
  • Defect: observations: own.length is capped at 100. The root and the earliest start come from that page only. TraceSummary.observations is documented as "how many observations the trace holds" (tracing/src/index.ts:75).
  • Scenario: a trace with 350 observations reports observations: 100. Its timestamp and fallback name come from whichever 100 the page returned.
  • Severity: later (fix the doc before publish).
  • Smallest fix: fetch the root with isRootObservation: true for name and timestamp, and either page through the cursor or rename the field observations_read.

F10. Visibility is decided by the first episode that created the node#

  • Where: app/chronicle/lib/artefactRef.ts:60-61, :97-98. Nodes are written with episode_path at notebook.ts:268, prototype.ts:266, tracing.ts:294, rispec.ts:205, and never updated.
  • Defect: an artefact held by several episodes is judged only by its first holder. The tracing node's address also pins the first attach's prompt version (tracing.ts:293).
  • Scenario: a notebook first attached to a community episode, then to public ep550, renders unresolved for visitors in ep550's own text, and its open door answers 404.
  • Severity: later.
  • Smallest fix: treat the artefact as visible when any episode with an edge to the node is visible, or when the referring page's episode is.

F11. rise://jgwill/episodes/... and the default branch point at GitHub pages that do not exist#

  • Where: packages/inquiry-weave/src/rise-dsl.ts:97-101, :107, rispec.ts:204, app/chronicle/lib/artefactRef.ts:111-117.
  • Defect: jgwill/episodes is ssh://mia@gaia:/srv/git/jgwill/episodes.git. gh repo view jgwill/episodes answers "Could not resolve to a Repository". Separately, defaultBranch = "main" 404s for any repository on master.
  • Scenario: a vessel spec outside miadi-chronicle/<folder>/ opens a GitHub 404. Every vessel spec node gets a dead github_url. riseInfoOf still says exists: true.
  • Severity: later.
  • Smallest fix: use blob/HEAD/ when there is no commit, and omit github_url for jgwill/episodes.
  • Where: packages/inquiry-weave/src/rispec.ts:181, :204.
  • Defect: the PATCH sets metadata.commit to the newest episode's commit and leaves github_url at the first commit.
  • Scenario: ep552 holds the spec at 27cf18f, then ep560 at abc123. The node says commit: abc123 with github_url: .../blob/27cf18f/....
  • Severity: later.
  • Smallest fix: patch github_url in the same call, or keep the commit only in the manifest.

F13. Langfuse prompts in folders cannot be held#

  • Where: packages/episodic-memory-schema/src/episode-tracing.ts:76, :127.
  • Defect: the id pattern excludes /. The SDK documents prompt names such as folder/subfolder/prompt-name (@langfuse/core Prompts.get).
  • Scenario: holding the prompt miadi/ep120-fork gets invalid_tracing from the door, and parseArtefactUri("miadi-prompt:ceremony/miadi/ep120-fork") returns null (proven).
  • Severity: later.
  • Smallest fix: allow / in prompt ids and encode it in the node id and the reference.

F14. Nothing stops an editing address from being written as the published url#

  • Where: packages/inquiry-weave/src/prototype.ts:60-64, packages/episodic-memory-schema/src/episode-prototype.ts:77-87.
  • Defect: any https URL is accepted as the published address. The reach rule depends on the person typing each URL into the right field.
  • Scenario: a writer pastes https://v0.app/chat/abc into "url". episode.yaml then serves the edit link to every reader.
  • Severity: later.
  • Smallest fix: refuse url === editor, and refuse hosts known to be editors (v0.app/chat, v0.dev/chat).

F15. A notebook's made_by publishes operator session names that the sessions door keeps private#

  • Where: packages/episodic-memory-schema/src/episode-notebook.ts:26 (example and fixtures), against app/chronicle/lib/episodeSessions.ts:4-5.
  • Defect: made_by carries a tmux name and a session id, and is served by the public GET .../notebooks and the room. A5 treats a session's name and pane as operator-only.
  • Scenario: a visitor reads "miadisabelle/deepdiver (tmux miadi-deepdiver, session 8a83dab6)" in a public episode.
  • Severity: later.
  • Smallest fix: decide the rule once. If session names are operator-only, keep the tool and commit in made_by and put the session on the node.

F16. A bare UUID in references: is read as a held notebook#

  • Where: packages/episodic-memory-schema/src/episode-notebook.ts:101, :226-231.
  • Defect: the "" prefix accepts any bare uuid.
  • Scenario (proven): references: ["d64a2fdf-…"], a review id written bare, comes back as a notebook with declared: "references". Today 0 of 308 reference strings across 150 manifests are bare uuids.
  • Severity: later.
  • Smallest fix: in the references loop, accept only the notebook URL and miadi-notebook:.

F17. Two parsers read miadi-prototype: differently#

  • Where: packages/episodic-memory-schema/src/episode-prototype.ts:72 against packages/inquiry-weave/src/artefact-dsl.ts:91.
  • Defect: the schema reads miadi-prototype:foo/bar as foo. The address parser refuses it.
  • Scenario (proven): references: [miadi-prototype:foo/bar] puts prototype foo in the room's card, while the same string in prose renders as an unresolved chip.
  • Severity: later.
  • Smallest fix: anchor the schema pattern (([^/?#\s]+)$), or have the schema reuse the same grammar.

F18. requireTracingRef does not check what its doc says it checks#

  • Where: packages/inquiry-weave/src/tracing.ts:59-64.
  • Defect: the doc says the reference and the separate fields "must agree". When the reference parses, the fields are ignored without comparison.
  • Scenario: {reference: "miadi-tracing:ceremony/trace/x", domain: "story"} is held in ceremony with no error.
  • Severity: later.
  • Smallest fix: compare the two, or drop the sentence.

F19. A trace with no id takes its name as its id#

  • Where: packages/episodic-memory-schema/src/episode-tracing.ts:198.
  • Defect: id ?? name applies to every resource kind, not only prompts.
  • Scenario: {domain: ceremony, resource: trace, name: ep120-run} is held as the trace id ep120-run, which the provider never finds.
  • Severity: later.
  • Smallest fix: fall back to name only when resource === "prompt".

F20. An older binding line can overwrite a newer session's team, agent, alias and host#

  • Where: packages/episodic-memory-schema/src/episode-sessions.ts:114, :118, :120-121.
  • Defect: these fields are taken from the incoming line even when it is older than the record. The doc at :10-11 says the latest event decides the team.
  • Scenario: lines read out of order give the team of the older line. The real file is append-ordered, so the risk is low.
  • Severity: later.
  • Smallest fix: apply the current.last_seen > at guard that name and pane already use.

F21. Dead code, unused packages and a claim no code implements#

  • Where:
    • packages/inquiry-weave/src/rise-dsl.ts:103-156
    • app/chronicle/lib/artefactRef.ts:17, :119
    • packages/episodic-memory-schema/src/episode-tracing.ts:41
    • packages/tracing-langfuse/README.md ("tags")
  • Defect:
    • riseWorkspacePath, riseLocalPath and KNOWN_RISE_WORKSPACES have no caller. The only use is the re-export in artefactRef.ts, and nothing imports it.
    • Nothing in app/, lib/ or packages/ imports @miadi/tracing or @miadi/tracing-langfuse.
    • "The project id is read from the keys and kept on the node" is not done anywhere.
    • The tracing-langfuse README says tags are answered. They are not (F4).
  • Scenario: a reader trusts the docs and expects a project id on tracing nodes, or tags in a trace summary. Neither exists.
  • Severity: later (the rise-dsl removal is part of the F1 fix).
  • Smallest fix: delete or wire each one, and correct the two docs.

F22. The open door's room anchors do not exist#

  • Where: app/chronicle/lib/artefactRef.ts:103.
  • Defect: it redirects to #prototypes and #tracing, but no element in the room has those ids.
  • Scenario: opening a tracing chip lands at the top of the room, not at the card.
  • Severity: later.
  • Smallest fix: give the two cards those ids.

F23. Empty titled cards on every episode#

  • Where: app/chronicle/components/EpisodeRoom.tsx:372, :378, :401, :404.
  • Defect: Card renders its title even when its child returns null.
  • Scenario: every visitor sees an empty "Sessions" box on every episode. Episodes that hold no prototypes, traces or specs also show empty "Prototypes", "Traces and prompts" and "Specifications" boxes.
  • Severity: later.
  • Smallest fix: render the Sessions card only when the API allowed the read, or let each child own its card.

F24. Artefact chips resolve one at a time#

  • Where: app/chronicle/lib/references.tsx:239-249, :101, :139.
  • Defect: each artefactInfoOf and riseInfoOf is awaited in turn, with a 4 s timeout each (artefactRef.ts:39). The gate regex rise: also matches "Enterprise:".
  • Scenario:
    • With the wheel tunnel half-open, a text with 10 artefact references takes about 40 s to render on the server.
    • Any text containing "Enterprise:" triggers a manifest load.
  • Severity: later.
  • Smallest fix: Promise.all, and gate on \brise:\/\/.

F25. An unrelated dependency bump rides in a #769 commit#

  • Where: pnpm-lock.yaml in 45d5ad25.
  • Defect: the commit also moves framer-motion and motion-dom 14.0.0 to 14.1.0, which comes from "framer-motion": "latest" at package.json:133. It is not #769 work.
  • Scenario: a revert of #769 also reverts that bump.
  • Severity: later.
  • Smallest fix: say so in the next commit, or pin the range.

F26. The Langfuse test's fake ignores the field selection#

  • Where: packages/tracing-langfuse/test/provider.test.mjs:22-34.
  • Defect: the fake getMany returns traceName whatever fields asks for. "the provider answers metadata only..." therefore asserts a name the real endpoint would not return.
  • Scenario: this is how F3 passed the tests.
  • Severity: fix with F3.
  • Smallest fix: make the fake drop trace_context fields unless they are requested, and assert the fields value.

F27. No test resolves an address through a door-created node#

  • Where: packages/reference-ui/test/reference-ui.test.mjs:254-277. artefactInfoOf and artefactNodeId have no test against door output.
  • Defect: the chip test hands in a made-up ArtefactInfo with exists: true for the @2 prompt.
  • Scenario: this is how F2 passed the tests.
  • Severity: fix with F2.
  • Smallest fix: an inquiry-weave test that attaches a versioned prompt with attachEpisodeTracing on the fake wheel and then looks up artefactNodeId(parseArtefactUri("miadi-prompt:…@2")) in wheel.store.nodes.

Risks#

R1. The reach rule ends at the wheel#

GET http://ilex:8040/api/nodes/prototype:<slug> has no authentication. Anyone who can reach the wheel (the loopback tunnel on gaia, or ilex.tail3b11eb.ts.net:8040 on the tailnet) reads editor_url and ui_url. That is acceptable while the tailnet is operators only. Write it down where the rule is stated, in the schema's prototype and tracing headers.

Ranked list (most severe first)#

  1. F1: the browser bundle break in ./references and reference-ui (blocks publish)
  2. F2: a versioned prompt never resolves (+F27)
  3. F6: resolve and open fail open
  4. F3: wrong trace name from Langfuse (+F26)
  5. F7: host paths in the pure schema
  6. F5: aliases not read from references:
  7. F4: listTraces name and tags
  8. F8: failed project cached forever
  9. F9: one-page trace summary
  10. F10: visibility by first holder
  11. F14: editing address accepted as url
  12. F11: dead GitHub targets for jgwill/episodes and main
  13. F12: spec node commit and link drift
  14. F13: foldered prompts refused
  15. F15: made_by reach
  16. F16: bare uuid read as a notebook
  17. F17: prototype parser divergence
  18. F23: empty cards
  19. F22: missing anchors
  20. F24: sequential resolution
  21. F18: unchecked "must agree"
  22. F19: trace id taken from name
  23. F20: out-of-order session merge
  24. F21: dead code and unimplemented claims
  25. F25: lockfile drift

Before tonight's publish#

These changes are needed before the five packages go to npm:

  • F1: take the fs-dependent workspace map out of rise-dsl.ts, so @miadi/inquiry-weave/references and @miadi/reference-ui bundle for a browser again. Prove it with the esbuild line in F1.
  • F2: build the tracing node id without the version, with the F27 test beside it.
  • F3: add trace_context to the Langfuse field selection, with the F26 fake fixed.
  • F5: read the two aliases in the schema's parseTracingRef.
  • F7: make HOST_PATH_REPOSITORIES an option that inquiry-weave passes, not an export of the schema.
  • F4: either implement or remove name and tags on listTraces.
  • F8: stop caching a failed project.
  • F6: fail closed in the two app routes before the :3335 rebuild.

Then run one next build, because components/ceremony/turn-text.tsx will be the first client bundle to compile the new references layer. Everything else can follow as patch releases.

🌸: Readers of a public episode keep seeing only what was meant for them, and the people who hold traces and prompts get correct names and working links.