diff --git a/src/Yavsc.Abstract/Blogspot/IBlogPost.cs b/src/Yavsc.Abstract/Blogspot/IBlogPost.cs
index 38a50b78f..c5a098e07 100644
--- a/src/Yavsc.Abstract/Blogspot/IBlogPost.cs
+++ b/src/Yavsc.Abstract/Blogspot/IBlogPost.cs
@@ -15,5 +15,23 @@ namespace Yavsc.Blogspot
// BlogPost entity implements this getter by mapping its
// ApplicationUser navigation to a BlogPostAuthorDto.
BlogPostAuthorDto? Author { get; }
+
+ ///
+ /// Whether this post is published. The server derives this
+ /// from the existence of a row in BlogSpotPublication
+ /// and serves it on the wire so clients (PostIt) can render
+ /// the current publication state without a follow-up request.
+ ///
+ /// This MUST be declared on the interface — not just on
+ /// the concrete BlogPost entity / BlogPostDto —
+ /// because the blog API's list endpoint returns
+ /// IEnumerable<IBlogPost>, and the blog API host
+ /// serialises with System.Text.Json, which emits only the
+ /// members of the declared (interface) type. When
+ /// IsPublished lived only on the concrete classes, STJ
+ /// dropped it from the list payload and PostIt read
+ /// false for every post, including published ones.
+ ///
+ bool IsPublished { get; set; }
}
}
diff --git a/src/Yavsc.Blogs.Tests/IBlogPostWireShapeTests.cs b/src/Yavsc.Blogs.Tests/IBlogPostWireShapeTests.cs
new file mode 100644
index 000000000..bb38725cd
--- /dev/null
+++ b/src/Yavsc.Blogs.Tests/IBlogPostWireShapeTests.cs
@@ -0,0 +1,87 @@
+using System.Text.Json;
+using Yavsc.Blogspot;
+using Yavsc.Models.Blog;
+
+namespace Yavsc.Blogs.Tests;
+
+///
+/// Wire-shape tests pinning that
+/// is carried on the JSON the blog API list endpoint emits.
+///
+/// The blog API host (Yavsc.Blogs/Program.cs) serialises
+/// with System.Text.Json (the .AddControllers() default — no
+/// AddNewtonsoftJson), and BlogApiController.GetBlogspot
+/// returns IEnumerable<IBlogPost>. System.Text.Json emits
+/// only the members of the declared type, so a property that
+/// exists solely on the concrete BlogPost entity is dropped from
+/// the payload. IsPublished used to be one such property: it was
+/// on BlogPost and BlogPostDto but not on
+/// , so the list response omitted it and PostIt
+/// read IsPublished == false for every post — including
+/// published ones.
+///
+/// These tests reproduce that serialisation path directly
+/// (declared type IEnumerable<IBlogPost> through
+/// ) and assert
+/// isPublished survives the round trip. They live in
+/// Yavsc.Blogs.Tests — the assembly that owns the blog API
+/// contract — so a regression at the wire boundary is caught here,
+/// where it manifested in production (PostIt showing a published
+/// post's status as false).
+///
+public sealed class IBlogPostWireShapeTests
+{
+ private static readonly JsonSerializerOptions WebDefaults
+ = new(JsonSerializerDefaults.Web);
+
+ [Fact]
+ public void IsPublished_is_emitted_when_list_is_serialised_as_IEnumerable_IBlogPost()
+ {
+ var post = new BlogPost
+ {
+ Id = 13,
+ Title = "Published",
+ AuthorId = "alice",
+ Article = "x",
+ };
+ post.IsPublished = true;
+
+ // Declared type mirrors GetBlogspot's return: IEnumerable.
+ IEnumerable list = new List { post };
+
+ // Serialize with the DECLARED type, the way ASP.NET Core's
+ // SystemTextJsonOutputFormatter hands context.ObjectType (the
+ // action return type) to JsonSerializer.Serialize.
+ var json = JsonSerializer.Serialize(list, typeof(IEnumerable),
+ WebDefaults);
+
+ using var doc = JsonDocument.Parse(json);
+ var element = doc.RootElement[0];
+ // Web defaults use camelCase, so the property is "isPublished".
+ Assert.True(element.TryGetProperty("isPublished", out var isPublished),
+ $"isPublished was dropped from the list payload. JSON: {json}");
+ Assert.True(isPublished.GetBoolean());
+ }
+
+ [Fact]
+ public void IsPublished_round_trips_through_BlogPostDto_on_the_client()
+ {
+ // The shape the client (PostIt) receives and deserialises with
+ // PropertyNameCaseInsensitive = true (cf. YavscApiClient).
+ var json = """
+ [{
+ "id": 13,
+ "title": "Published",
+ "article": "x",
+ "authorId": "alice",
+ "isPublished": true
+ }]
+ """;
+
+ var posts = JsonSerializer.Deserialize>(json,
+ new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
+
+ var post = Assert.Single(posts);
+ Assert.True(post.IsPublished);
+ }
+}
\ No newline at end of file