feat(commands): adopt capability shapes in the TUI session-export slice (FEAT-025) - #6096
Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
54fa34a to
5b0578a
Compare
…sanitizer Contract side: `CommandSessionExportContext` with seven operation-granular, `&self` delegates over contract-owned DTOs, the `SESSION_EXPORT` capability at bit 15, the envelope slot, and the `export` projections (metadata, typed transcript with D9 omission variants, semantic restore points, turn handoff). The raw capability accessor is `#[cfg(test)]` and a canary pins the fact that bits 0-15 are now fully allocated - the next capability requires widening to `u32`, which FEAT-026 owns. Shared sanitizer: the pure ANSI/URL/text/secret redaction primitives move to `codewhale-secrets` (`redact` + `sanitize`) so portable command helpers have one acyclic implementation with no TUI, client, or config dependency. The bodies are relocated verbatim; `config::persistence` re-exports to keep its public API stable, and `tui::client` / `tui::osc8` delegate so their callers do not drift. No behaviour change: the moved algorithms, ordering, sensitive-key vocabulary and results are byte-identical. Generated with CodeWhale
TUI side of the slice. `SessionExportAdapter` becomes the sole owner of concrete export machinery - metadata derivation, authoritative/visible-history projection, semantic restore-point projection, the shared `turn_handoff_markdown` renderer, clipboard mode/recovery/delivery, and protected destination resolution/writing - and the envelope exposes it only when `SESSION_EXPORT` is declared. Hidden reasoning bodies, signatures, and inline/local image payloads are dropped while the projection is built (D9). Host services that must outlive the physical move live in `commands/session_export_host.rs`, outside `groups/session`: the shared `last-copy.md` recovery writer now reused by `/copy`, and the protected export-destination resolver/writer. Their algorithms, check order, error text, and platform behaviour are the baseline implementations relocated unchanged. The portable handler owns parsing, document rendering, redaction, restore-point correlation, operation sequencing and every user-visible string. It renders from an owned projection and redacts JSON in place, so no payload is copied twice. `/structcopy` only rewires its sanitizer import and stays legacy; its stale "export seam" prose is corrected. `/export` keeps its declared order, name, alias, usage, and discovery surface. Generated with CodeWhale
5b0578a to
8a8de85
Compare
|
Resolved in The single finding was not a real credential. It came from moving the private-key redaction test out of // Assemble the PEM markers at runtime so the source file never
// contains a literal private-key header for a scanner to match.
let begin = ["-----BEGIN RSA", " PRIVATE KEY-----"].concat();— and the rewritten test used the literal header instead. The relocation preserved the algorithm but dropped the convention, which is exactly the class of thing a "verbatim move" is supposed to carry across. Fixed by restoring the repository's own idiom (runtime assembly of the PEM markers and the token prefix) rather than widening Paulo Aboim Pinto |
8a8de85 to
e96013a
Compare
|
Windows failure fixed in Cause: this PR adds four This is precisely the class your own Fix: declare the new fixtures Paulo Aboim Pinto |
…nd gates Behaviour preservation is proven rather than claimed. Four goldens were captured from the pre-refactor implementation at `3f3aa9ed7` by dispatching frozen fixtures through the baseline public `/export` seam in a scratch worktree: - authoritative transcript with no restore repository (header, every content variant, D9 omission markers, redaction, ANSI stripping), - the turn handoff, - the visible-history fallback over a workspace with recorded restore points, - correlation against recorded restore points (table, newest-first order, `tool` snapshots listed but not correlated, the ambiguous double match, and the no-match line). Only genuinely clock-derived fields are normalised (the export stamp, the turn header stamp, and - for the recorded states - the snapshot id and `Recorded (UTC)` cell, because `git commit-tree` uses a wall-clock date). A unit test pins the normaliser so it cannot quietly widen. Everything else is byte-compared. Also here: the exhaustive `ContextParts` assertion that makes "no unrelated facet" a compile-time guarantee, the shared host-bound test-support module, the token-aware extraction audits plus their own unit test, and the boundary gate extension that keeps `codewhale-secrets` free of any TUI edge. Carries one unrelated, disclosed repair: a bounded retry for a fork/exec `ETXTBSY` race in the `wl-paste` regression, kept because the configured workspace gate was intermittently red without it. Generated with CodeWhale
e96013a to
54d9261
Compare
|
macOS failure fixed in
Cause: these two relocated tests call the facet operation Fix: the adapter-level tests now build their workspace with a I also added The production behaviour is unchanged: the symlink rejection is a verbatim relocation of the baseline, and Paulo Aboim Pinto |
|
Ready for review. All checks are green on
Two of the three were repository conventions that the relocation failed to carry over rather than code defects, which is the useful thing to know when reviewing a "structural migration": the algorithm moved correctly, the surrounding discipline did not. Both are now documented in the code and pinned by tests. Final state:
Please take a look when you have time. Paulo Aboim Pinto |
FEAT-025: adopt command shapes in the TUI session-export slice
Rewires
/export(alias/daochu) through the portable command contract while the command filestays in
codewhale-tui. Structural migration only: no user-visible behaviour change.Tracking: EPIC-006 / FEAT-025 in umbrella Hmbown/CodeWhale#5316.
This is one bounded slice of EPIC-006, which extracts the TUI command system into
codewhale-commandsin two passes: adopt capability shapes in place, group by group, then move thealready-decoupled code. This PR does the first pass for session export only.
What changes
Contract (
codewhale-command-contract)CommandSessionExportContext: seven operation-granular,&selfdelegates returningcontract-owned DTOs (metadata, transcript projection, semantic restore points, turn handoff,
clipboard mode/recovery/delivery, destination resolution/writing).
SESSION_EXPORTcapability, declared by/exportand nothing else. The TUI bridge exposes onlythat facet.
inline/local image payloads are replaced by typed omission markers while the projection is built,
so they never enter the DTOs.
#[cfg(test)]; a canary pins the fact that bits 0-15 are now fullyallocated (see "Reviewer notes").
TUI (
codewhale-tui)SessionExportAdapteris the sole owner of concrete export machinery: metadata derivation,authoritative/visible-history projection, semantic restore-point projection, the existing
turn_handoff_markdownrenderer, clipboard mode/recovery/delivery, and protected destinationresolution/writing.
commands/session_export_host.rs,outside
groups/session: the sharedlast-copy.mdrecovery writer (now reused by/copy) andthe protected export-destination resolver/writer. Algorithms, check order, error text and platform
behaviour are the baseline implementations relocated unchanged.
operation sequencing and every user-visible string. It renders from an owned projection and redacts
JSON in place, so no payload is copied twice.
/copyand/structcopyonly rewire their helper imports; neither is migrated.One shared sanitizer. The pure ANSI/URL/text/secret-redaction primitives move to
codewhale-secrets(redact+sanitize) so portable command helpers have one acyclicimplementation with no TUI, client, or config dependency. Bodies are relocated verbatim;
config::persistencere-exports to keep its public API stable, andtui::client/tui::osc8delegate so their callers cannot drift.
Preservation evidence
Behaviour is preserved, and this is proven rather than asserted.
worktree by dispatching frozen fixtures through the baseline public
/exportseam: authoritativetranscript with no restore repository (header, every content variant, omission markers, redaction,
ANSI stripping); the turn handoff; the visible-history fallback over a workspace with recorded
restore points; and correlation against recorded restore points (table, newest-first order,
toolsnapshots listed but not correlated, the ambiguous double match, the no-match line). Only genuinely
clock-derived fields are normalised, with a unit test pinning the normaliser so it cannot quietly
widen. Everything else is compared byte-for-byte. Re-capturing them from the implementation under
test would make the comparison a tautology; the fixture header says so.
redact.rsis a byte-identical move of the 586-lineblock from
config::persistence;strip_ansi_impl/utf8_seq_lenare byte-identical to the formerosc8bodies;redact_url_for_displayis identical apart fromreqwest::Url→url::Url(thesame type); the parser and the protected-file/recovery writers are literal copies.
refusal (and
--force), private permissions, symlink rejection, unprotected-path errors,clipboard→recovery ordering, and all usage-error text.
Least authority and extraction readiness
/exportdeclares exactlySESSION_EXPORT; noPRESENTATIONfacet, no new catalog string.Command capability unavailable: session_export; there is no.expect()or panic on the facet path.
ContextPartsslots, so "exposes no unrelatedfacet" is a compile-time guarantee: a new facet fails to compile until it is classified, and every
slot is asserted absent in one place.
App, clipboard, snapshot, filesystem, history, or turn-handoff type crosses the facet.1 << 15the backing storage staysu16andNONEsemantics are unchanged.Extraction proven by compilation. The handler plus its 21 portable tests were compiled in a
throwaway crate outside the TUI, with only a stand-in for the temporary FEAT-037
CommandResult.Library and test targets build and all 21 tests pass with no
codewhale-tuiin the dependencygraph. The only adjustment needed was making the stand-in faithful to
CommandResult::error(
Error: {msg}prefix) - which is the point: that outcome type is the one TUI-owned thing the slicetouches. Host-bound tests and fixtures stay at the
commandsroot, outside the group that will move.Post-implementation audit
The slice was audited against the pristine pre-change tree before this PR, and again after the audit
fixes. No functional defect was found. The findings that changed the code are folded into the commits
above; the ones that did not are recorded here rather than silently dropped:
"user"is_user_rolecarries the exactRole::Usercomparison, soRole::Unrecognized("user")is not treated as a user turnexport::<helper>seam comments in/structcopyUnreadablerestore arm has no adapter-level testgitbinary cannot be resolved (SnapshotRepo::listmaps a failinggit logto an empty list), anddependencies::Git::resolveis process-cached, so any test would be flaky. Behaviour is unchanged from the baseline.SharedCommandHostlendsAppthrough aRefCell, and adynfacet cannot return data borrowed from a temporaryRef. Removing it needs a host proxy that can lend a borrowed projection; the avoidable second copy was removed instead.Scope disclosure
crates/tui/src/tui/clipboard.rscarries an unrelated, test-only bounded retry for a fork/execETXTBSYrace in thewl-pasteregression. It is kept because the configured workspace gate wasintermittently red without it, and it is disclosed rather than hidden. There is no equivalent change
on
mainto reconcile against. Happy to split it into its own PR on request.Not fixed here
CommandCapabilitieswidening tou32(see reviewer notes) - owned by the next slice.Cowexport projection, which needs the host-proxy change described above.CommandResult; noAppAction,callback, or deferred action was added. The root
sessionmigration frontier remains pending.Verification
Rebased onto current
main; all gates re-run at the rebased head:cargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features --locked -- -D warnings -A clippy::uninlined_format_args -A clippy::too_many_arguments -A clippy::unnecessary_map_orsh scripts/with-hermetic-test-home.sh cargo nextest run --workspace --all-features --locked --profile cish scripts/with-hermetic-test-home.sh cargo test --workspace --all-features --locked --doccargo build --release --locked -p codewhale-cli -p codewhale-tuicargo test -p codewhale-command-contract --lib --lockedpython3 scripts/test_check_command_migration_manifest.py+ live check[config, core, debug, session]python3 scripts/test_check_command_crate_boundaries.py+ live checkcodewhale-command-contractorcodewhale-secretspython3 scripts/test_ci_migration_wiring.py/python3 scripts/check-tui-locale-parity.pycargo doc -p codewhale-secrets --no-deps --lockedReviewer notes
this slice was in progress. The branch is three logical commits on current
main. Adopting thecodewhale-modelsandcodewhale-localizationextractions needed four import-path updates, andthe
CmdExportDescriptionEnglish string was reworded upstream (French unchanged); the tests pinthe catalog values.
CommandCapabilitieshas no free bits left.SESSION_EXPORT = 1 << 15was the last one. Thenext slice needs its own exact-minimum facet, so it must widen the backing storage to
u32(orjustify reusing a bit, which would break the least-capability invariant this epic is built on). A
canary test asserts the space is exactly full so this is a deliberate change, not an overflow.
Provider,Model,Mode, the turn route line, and forthe recorded states the snapshot id and timestamp cell). A future change to the default config or
the handoff header will fail them by design; they must be re-captured from a verified baseline.
No-Issue: FEAT-025 is tracked in umbrella issue #5316, which remains open for the remaining EPIC-006 work.
Paulo Aboim Pinto