Skip to content

Commit 4248b36

Browse files
authored
Merge pull request #592 from Chris0Jeky/feature/218-transcript-capture-source
Add transcript paste/file capture source for Inbox pipeline
2 parents 8b52b65 + ca62ec9 commit 4248b36

File tree

7 files changed

+814
-18
lines changed

7 files changed

+814
-18
lines changed

backend/src/Taskdeck.Application/DTOs/CaptureContracts.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public static class CaptureRequestContract
3535
public const string RequestTypeV1 = "inbox.capture.v1";
3636
public const int CurrentSchemaVersion = 1;
3737
public const int MaxRawTextLength = 20_000;
38+
public const int MaxTranscriptTextLength = 51_200;
3839
public const int MaxTitleHintLength = 240;
3940
public const int MaxExternalRefLength = 2_048;
4041
public const int MaxPromptVersionLength = 64;
@@ -170,11 +171,12 @@ public static Result<CapturePayloadV1> ValidatePayload(CapturePayloadV1 payload)
170171
return Result.Failure<CapturePayloadV1>(ErrorCodes.ValidationError, "Capture text cannot be empty");
171172
}
172173

173-
if (payload.Text.Length > MaxRawTextLength)
174+
var effectiveMaxLength = IsTranscriptSource(payload.Source) ? MaxTranscriptTextLength : MaxRawTextLength;
175+
if (payload.Text.Length > effectiveMaxLength)
174176
{
175177
return Result.Failure<CapturePayloadV1>(
176178
ErrorCodes.ValidationError,
177-
$"Capture text cannot exceed {MaxRawTextLength} characters");
179+
$"Capture text cannot exceed {effectiveMaxLength} characters");
178180
}
179181

180182
if (payload.TitleHint?.Length > MaxTitleHintLength)
@@ -419,6 +421,11 @@ private static Result ValidateForbiddenIdentityFields(JsonElement root, bool all
419421
return Result.Success();
420422
}
421423

424+
public static bool IsTranscriptSource(CaptureSource source)
425+
{
426+
return source is CaptureSource.TranscriptPaste or CaptureSource.TranscriptFile;
427+
}
428+
422429
private static bool IsSupportedSourceSurface(string sourceSurface)
423430
{
424431
return sourceSurface.Equals("chat", StringComparison.OrdinalIgnoreCase) ||

backend/src/Taskdeck.Domain/Enums/CaptureSource.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ public enum CaptureSource
1010
TranscriptPaste = 2,
1111
Import = 3,
1212
Voice = 4,
13-
MeetingIntegration = 5
13+
MeetingIntegration = 5,
14+
TranscriptFile = 6
1415
}

backend/tests/Taskdeck.Application.Tests/Services/CaptureRequestContractTests.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,4 +422,136 @@ public void SanitizeProvenanceMetadata_ShouldReturnFallback_WhenValueIsMissing()
422422

423423
sanitized.Should().Be("unknown");
424424
}
425+
426+
[Fact]
427+
public void IsTranscriptSource_ShouldReturnTrue_ForTranscriptPaste()
428+
{
429+
CaptureRequestContract.IsTranscriptSource(CaptureSource.TranscriptPaste).Should().BeTrue();
430+
}
431+
432+
[Fact]
433+
public void IsTranscriptSource_ShouldReturnTrue_ForTranscriptFile()
434+
{
435+
CaptureRequestContract.IsTranscriptSource(CaptureSource.TranscriptFile).Should().BeTrue();
436+
}
437+
438+
[Theory]
439+
[InlineData(CaptureSource.Typed)]
440+
[InlineData(CaptureSource.Paste)]
441+
[InlineData(CaptureSource.Import)]
442+
[InlineData(CaptureSource.Voice)]
443+
[InlineData(CaptureSource.MeetingIntegration)]
444+
public void IsTranscriptSource_ShouldReturnFalse_ForNonTranscriptSources(CaptureSource source)
445+
{
446+
CaptureRequestContract.IsTranscriptSource(source).Should().BeFalse();
447+
}
448+
449+
[Fact]
450+
public void ValidatePayload_ShouldAllowLargerText_ForTranscriptPasteSource()
451+
{
452+
var textLength = CaptureRequestContract.MaxRawTextLength + 1;
453+
var payload = new CapturePayloadV1(
454+
CaptureRequestContract.CurrentSchemaVersion,
455+
CaptureSource.TranscriptPaste,
456+
new string('t', textLength));
457+
458+
var result = CaptureRequestContract.ValidatePayload(payload);
459+
460+
result.IsSuccess.Should().BeTrue();
461+
result.Value.Text.Should().HaveLength(textLength);
462+
}
463+
464+
[Fact]
465+
public void ValidatePayload_ShouldAllowLargerText_ForTranscriptFileSource()
466+
{
467+
var textLength = CaptureRequestContract.MaxRawTextLength + 1;
468+
var payload = new CapturePayloadV1(
469+
CaptureRequestContract.CurrentSchemaVersion,
470+
CaptureSource.TranscriptFile,
471+
new string('t', textLength));
472+
473+
var result = CaptureRequestContract.ValidatePayload(payload);
474+
475+
result.IsSuccess.Should().BeTrue();
476+
result.Value.Text.Should().HaveLength(textLength);
477+
}
478+
479+
[Fact]
480+
public void ValidatePayload_ShouldFail_WhenTranscriptTextExceedsTranscriptMaxLength()
481+
{
482+
var payload = new CapturePayloadV1(
483+
CaptureRequestContract.CurrentSchemaVersion,
484+
CaptureSource.TranscriptPaste,
485+
new string('t', CaptureRequestContract.MaxTranscriptTextLength + 1));
486+
487+
var result = CaptureRequestContract.ValidatePayload(payload);
488+
489+
result.IsSuccess.Should().BeFalse();
490+
result.ErrorCode.Should().Be(ErrorCodes.ValidationError);
491+
result.ErrorMessage.Should().Contain("cannot exceed");
492+
result.ErrorMessage.Should().Contain(CaptureRequestContract.MaxTranscriptTextLength.ToString());
493+
}
494+
495+
[Fact]
496+
public void ValidatePayload_ShouldAcceptTranscriptTextAtExactMaxLength()
497+
{
498+
var payload = new CapturePayloadV1(
499+
CaptureRequestContract.CurrentSchemaVersion,
500+
CaptureSource.TranscriptFile,
501+
new string('t', CaptureRequestContract.MaxTranscriptTextLength));
502+
503+
var result = CaptureRequestContract.ValidatePayload(payload);
504+
505+
result.IsSuccess.Should().BeTrue();
506+
}
507+
508+
[Fact]
509+
public void ParsePayload_ShouldParseTranscriptFileSource()
510+
{
511+
var payload = """
512+
{
513+
"version": 1,
514+
"source": "transcriptFile",
515+
"text": "meeting transcript content"
516+
}
517+
""";
518+
519+
var result = CaptureRequestContract.ParsePayload(payload);
520+
521+
result.IsSuccess.Should().BeTrue();
522+
result.Value.Source.Should().Be(CaptureSource.TranscriptFile);
523+
result.Value.Text.Should().Be("meeting transcript content");
524+
}
525+
526+
[Fact]
527+
public void ParsePayload_ShouldParseTranscriptFileSourceAsNumeric()
528+
{
529+
var payload = """
530+
{
531+
"version": 1,
532+
"source": 6,
533+
"text": "meeting transcript content"
534+
}
535+
""";
536+
537+
var result = CaptureRequestContract.ParsePayload(payload);
538+
539+
result.IsSuccess.Should().BeTrue();
540+
result.Value.Source.Should().Be(CaptureSource.TranscriptFile);
541+
}
542+
543+
[Fact]
544+
public void ValidatePayload_ShouldEnforceStandardLimit_ForNonTranscriptSources()
545+
{
546+
var payload = new CapturePayloadV1(
547+
CaptureRequestContract.CurrentSchemaVersion,
548+
CaptureSource.Typed,
549+
new string('x', CaptureRequestContract.MaxRawTextLength + 1));
550+
551+
var result = CaptureRequestContract.ValidatePayload(payload);
552+
553+
result.IsSuccess.Should().BeFalse();
554+
result.ErrorCode.Should().Be(ErrorCodes.ValidationError);
555+
result.ErrorMessage.Should().Contain(CaptureRequestContract.MaxRawTextLength.ToString());
556+
}
425557
}

0 commit comments

Comments
 (0)