feat(ui): publish the redaction renderer on its own subpath - #94
Conversation
It is the only test here with no sibling source, so the paired-file name read as a missing component. The rule-named form matches how the cross-cutting tests in packages/brand are named.
The component reached the package only through the root entry, and every consumer takes ui through a subpath, so the client half of agent-app's reversible redaction was unreachable in practice.
🦋 Changeset detectedLatest commit: 95d4519 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🤖 AI Code Review (ensemble)SummaryThis PR publishes the redaction renderer on its own ./redaction subpath by adding it to package.json exports and tsup config, plus adds a doc header and a changeset. The changes are minimal, self-consistent, and low-risk. No Issues Found✅ APPROVEThe tsup entry and package.json export are aligned, the JSDoc header is informational, and the test rename is cosmetic. No runtime or type-level behavior changes exist. The P3 concern about dist/ files not being committed is consistent with the existing pattern (e.g., the nav subpath) and standard for monorepos where builds run before publish, so it doesn't warrant an issue entry. Synthesized by Sokuza AI from multiple independent reviewers |
tangletools
left a comment
There was a problem hiding this comment.
🟢 Value Audit — sound
| Verdict | sound |
| Coverage | 2 of 2 lenses (value, usefulness) |
| Concerns | 0 (none) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 65.9s (2 bridge agents) |
| Total | 65.9s |
💰 Value — sound
Publishes the already-shipped RedactedDocument on a ./redaction subpath (exports + tsup) so it is reachable the way every consumer actually imports this package, plus a header docblock and a rule-named test rename — all in-grain, additive, no rework.
- What it does: Adds a ./redaction entry to packages/ui/package.json:105-109 (mirroring the {types,import,default} shape of the other 15 subpaths) and a one-line redaction entry to packages/ui/tsup.config.ts:21, so import { RedactedDocument } from "@tangle-network/ui/redaction" resolves. Adds a 12-line header docblock to packages/ui/src/redaction/index.ts:1-12 recording that this is the client half of agent-app's
- Goals it achieves: (1) Make RedactedDocument actually reachable: it was re-exported from src/index.ts:13 but had no subpath in exports, and the PR's premise (no consumer imports the bare root) is consistent with the package shipping 15 subpaths precisely so apps avoid the root barrel. (2) Align the orphan test's name with the cross-cutting-test convention — packages/brand/src/styles uses tokens.test.ts, theme-parity
- Assessment: Clean, minimal, in-grain. The exports entry is byte-for-byte the same shape as the existing 15; the tsup entry matches the existing entry map one-for-one; the root re-export in src/index.ts:13 stays consistent with primitives/chat/run/etc. which are all both root-re-exported AND subpath-published (nav and sdk-hooks are subpath-only, so the package already mixes both styles — redaction fits the mor
- Better / existing approach: none — this is the right approach. Searched packages/ui/src/index.ts (re-exports redaction at line 13), packages/ui/package.json exports map (15 prior subpaths all using the identical three-field shape), tsup.config.ts (entry map pattern), and packages/brand/src/styles/*.test.ts (tokens/theme-parity/named-themes naming). The change copies the existing pattern verbatim; there is no existing equival
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 2
- Bridge warning: opencode/kimi-for-coding/k2p7: opencode: opencode error
🎯 Usefulness — sound
Additive publish-surface fix: registers ./redaction in the exports map and tsup entries so an already-shipped module becomes reachable through the subpath every consumer uses, matching the shape of sixteen sibling subpaths.
- Integration: Wires correctly. The new
./redactionentry (packages/ui/package.json:106-110) points at./dist/redaction.{d.ts,js}, andredaction: src/redaction/index.tsis now in the tsup entry map (packages/ui/tsup.config.ts:21), so the build produces exactly those artifacts. scripts/validate-dist.mjs:18-33 walksmanifest.exportsand fails the build if any target file is missing post-build, so the wirin - Fit with existing patterns: Follows the established pattern exactly. Every other subpath (
./chat,./run,./files, … ./nav) uses the identical{types, import, default}triple pointing at./dist/.{d.ts,js}`, and this entry is the same shape verbatim. The tsup entry slots into the same entry map. No competing or duplicate surface. The test rename (faint-text.test.tsx -> faint-text-tokens.test.tsx, similarity 100 - Real-world viability: This is a packaging/wiring change with no runtime semantics — the component was authored in a prior change and is out of scope here. The only behavior introduced is 'make the built artifact importable via a subpath,' which is binary: the import resolves or it doesn't, and the validate-dist gate enforces it on every build. Nothing here introduces a happy-path-only risk.
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 1
No concerns — sound change, no better or existing approach found. ✅
What this audit checks
It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.
| Pass | What it asks |
|---|---|
| Heuristic | Vague title? Whitespace-only or cruft-bearing diff? (content signals only) |
| Duplication | Do added function/class names already exist elsewhere in the repo? |
| Value Audit | What does it do? What goal does it achieve? Is it good? Better architecture or already-exists? |
| Usefulness Audit | Does it integrate and fit? Will it hold up in real use and actually get used? |
Findings are concerns, not blocks — the human reviewer decides what to do with them.
✅ No Blockers —
|
| opencode GLM 5.2 | opencode DeepSeek v4 Pro | opencode DeepSeek v4 Flash | aggregate | |
|---|---|---|---|---|
| Readiness | 92 | 95 | 86 | 86 |
| Confidence | 80 | 80 | 80 | 80 |
| Correctness | 92 | 95 | 86 | 86 |
| Security | 92 | 95 | 86 | 86 |
| Testing | 92 | 95 | 86 | 86 |
| Architecture | 92 | 95 | 86 | 86 |
Reviewer score is advisory once the run is complete and the verdict has no blockers.
Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision.
🟡 LOW Verbose changeset body for public CHANGELOG — .changeset/lucky-donuts-search.md
The second paragraph (lines 7-9) details cross-package architecture (agent-app pairing, segment-type rationale). Changesets prose is published verbatim to the package's CHANGELOG.md; readers of @tangle-network/ui will see an internal agent-app narrative that has no actionable meaning for them. Consider trimming the body to the one-line summary at line 5 plus a short 'adds ./redaction subpath exporting RedactedDocument' note. Stylistic, not blocking.
🟡 LOW Header asserts agent-app contract facts that nothing in this repo verifies — packages/ui/src/redaction/index.ts
The added header claims @tangle-network/agent-app builds the redaction document with each original encrypted, reveals through revealSpan, and carries a
cipheron its masked segment. agent-app is not a workspace member (packages/ contains only brand and ui) and not a dependency of @tangle-network/ui (grep of package.json), so no import, type, or test in this repo guards those claims — they can drift out of sync with agent-app without any compile or test failure. Impact: documentation-only, so low; it names a contract boundary between two packages with no enforcing link. Fix: if the claim is load-bearing, add a comment pointer to the agent-app source path (e.g. packages/agent-app/src/redact) or a type-compat test; otherwise trim to what the browser type alone proves.
🟡 LOW Header duplicates redacted-document.tsx's existing block comment — packages/ui/src/redaction/index.ts
index.ts:1-12 re-explains the same pairing/reveal flow already documented at redacted-document.tsx:5-15 (server-side authz+audit via revealSpan, cipher stays server-side, structural type without cipher). Two copies of the same contract invite drift between them. Impact: maintainability nit. Fix: keep the index.ts header to one or two sentences and let redacted-document.tsx own the full contract, or drop the redundant sentence in index.ts that repeats the reveal/audit flow.
🟡 LOW Redaction renderer ships without its own test coverage — packages/ui/tsup.config.ts
The new redaction entry exposes src/redaction/redacted-document.tsx (RedactedDocument/RedactedChip: 150 lines with masked/loading/revealed/denied chip states) as a public subpath, but the package has no test for it — the only test touched in this PR is the unrelated faint-text-tokens rename in packages/ui/src/run/. The chip's async reveal() branches (r.ok && value !== undefined vs denied vs catch) are exercised by no test. This is not a blocker for the one-line config change itself; the renderer predates this PR (added in 46592b3). Recommend a follow-up component test once the module is consumer-facing.
tangletools · 2026-08-13T19:04:41Z · trace
tangletools
left a comment
There was a problem hiding this comment.
✅ Approved — 4 non-blocking findings — 95d4519d
Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision. | Full multi-shot audit completed 4/4 planned shots over 5 changed files. Global verifier still owns final merge decision.
Full immutable report for this review: trace
Summary comment for this run: full summary
tangletools · 2026-08-13T19:04:41Z · immutable trace
Problem
src/redactionships but cannot be reached. It is re-exported fromsrc/index.tsand has no./redactionentry inpackage.json#exports, so the only way in is the bare package root — and no consumer imports the bare root. Every app takes this package through a subpath, so the module is published and unreachable at the same time.src/primitives/faint-text.test.tsxis the only test inpackages/uiwith no sibling source; the other sixteen are all paired with a component. The paired-file name therefore reads as a missingfaint-text.tsx.What this changes
./redactionjoins the exports map and the tsup entries, soimport { RedactedDocument } from "@tangle-network/ui/redaction"resolves. The change is additive: no existing export moves or changes shape.The module also gains a header recording what it is for, because that intent survived only in a prop comment.
RedactedDocumentis the client half of agent-app's reversible redaction — agent-app builds a document of masked spans and reveals one at a time throughrevealSpan, where the authorization and the audit record happen, and this component renders that document and calls back for a reveal. The header also states why the two segment types differ: agent-app's masked span carries thecipherand this one does not, because the ciphertext stays on the server and the type a browser holds must not name it.The test is renamed to
faint-text-tokens.test.tsx, after the rule it asserts. It is a pure rename with no content change. The name follows how the cross-cutting tests inpackages/brandare named —contrast,theme-parity,tokens— rather than the component-pair convention it was sitting in.What this deliberately does not change
The third item on the task asked to drop five unreferenced helpers:
detectFileFormat,getCodeLanguage,getFormatLabel,getSyntaxLanguageandparseToolEvent. They stay, for two reasons.All five are load-bearing inside this package —
file-preview.tsx,file-artifact-pane.tsx,write-file-preview.tsxanduse-tool-call-stream.tsuse them — so "drop" could only ever mean un-export, never delete.More to the point, the measurement that called them unused was taken while they were unreachable. These are exactly the names
@tangle-network/sandbox-uidropped from its hand-typed shims, fixed only in sandbox-ui 0.101.1. Until then they could not be imported through the path every consumer actually uses, and no app imports@tangle-network/ui/filesor@tangle-network/ui/rundirectly. Absence of use under those conditions is not evidence that the helpers are unwanted.Removing them would be a breaking API change, would red sandbox-ui's export-parity gate, and would force a second sandbox-ui release one day after the names were added — for no measured gain, on helpers that pair naturally with the exported
FilePreviewandToolCallFeed.Validation
pnpm typecheckcleanpnpm test— 240 tests across 21 filespnpm buildpasses;validate-distreports 17 exports, up from 16pnpm test:package— the packed tarball builds in a clean consumer across all 17 exports, which is what proves the new subpath resolves rather than merely being configuredUnrelated to this branch:
packages/brand/src/styles/contrast.test.tsis currently untracked in my working copy and fails because it importstokenRgb, which does not exist onmain. The numbers above were measured with that file set aside; it is not part of this change.Closes tangle-network/agent-dev-container#5276