You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
S11.04 (#400) completed the Core sweep for the six file-scope-singleton classes
that had no public storage type. This story finishes the Core sweep by migrating
the three remaining storage-cast classes plus the two TU-internal
sub-components that BlockStore composes:
Public, storage-cast today — pool migration + public Storage-type deletion:
RecordStore and BlockSequence get the canonical 3-TU split too — _Init(self, ...) → _Create(...) + _Destroy(handle), vending pool slots.
Their headers and TUs remain in Core/Source/ (TU-internal). BlockStore holds
pointers to them, not embedded structs.
Pool sizes for RecordStore and BlockSequence are hardcoded to SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE — they are 1:1 with BlockStore by
construction and don't merit their own integrator-facing tunable.
Per-class delta target unchanged from S11.02: *Static.c ends at ~3 file-scope
functions (_Create, _Destroy, IndexFromHandle) + the CleanupAtIndex
bridge + per-class config validation where one exists. No new MISRA findings.
Decisions locked in pre-raise
1. Fallback objects reuse S11.04's shared GoF nulls where they fit.
Class
Fallback
BlockStore
SolidSyslogNullStore (already public, _Get-only after S11.04)
FileBlockDevice
Class-private — BlockDevice has no shared GoF null and FileBlockDevice is the only BlockDevice implementor today
StreamSender
SolidSyslogNullSender (shipped in S11.04)
RecordStore
Class-private — TU-internal, no GoF null possible
BlockSequence
Class-private — TU-internal, no GoF null possible
The TU-internal class-private fallbacks for RecordStore and BlockSequence can
be minimal: their _Create returning the fallback is observable only by
BlockStoreStatic.c, which already handles pool-exhausted Create-time failure
by returning SolidSyslogNullStore_Get() from its own outer Create.
2. Pool size defaults.
Class
Tunable
Default
BlockStore
SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE
1U
FileBlockDevice
SOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE
1U
StreamSender
SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE
1U
RecordStore
none — sized to SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE directly
tracks BlockStore
BlockSequence
none — sized to SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE directly
tracks BlockStore
By construction, every successful BlockStore Create consumes one RecordStore
slot and one BlockSequence slot from their respective pools. Bumping SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE automatically grows all three together.
RecordStore and BlockSequence emit a class-private POOL_EXHAUSTED message if
ever called more times than expected — they shouldn't be, but the guard
catches a future refactor that breaks the 1:1 invariant.
3. Three-TU split per class — unchanged from project_e11_three_tu_split:
SolidSyslog<Class>.c (or <Class>.c for TU-internal) — vtable + private _Initialise/_Cleanup.
SolidSyslog<Class>Private.h (or <Class>Private.h) — struct definition + _Initialise/_Cleanup decls. TU-internal; tests must not include.
For the two TU-internal sub-components, the <Class>Static.c exports <Class>_Create and <Class>_Destroy with external linkage so BlockStoreStatic.c can link to them. There is no SolidSyslog<Class>-style
public header.
4. BlockStore's slot struct holds pointers, not embedded structs, after
this story:
BlockStore_Create calls RecordStore_Create and BlockSequence_Create to obtain
the handles, in that order. BlockStore_Cleanup calls their _Destroys in
reverse order. The 1:1 invariant means under normal operation neither inner
Create ever returns its fallback; if either does, BlockStore_Create routes to SolidSyslogNullStore_Get() and emits its own POOL_EXHAUSTED error.
5. No new MISRA suppressions. E11 invariant. Storage-cast MISRA deviations
get removed by this story (the whole point) — track which deviations
evaporate.
6. File-scope statics follow the NAMING.md sweep landed in S11.04 — <Class>_InUse, <Class>_Pool, <Class>_Allocator from the start. No bare InUse/Pool/Allocator in any new *Static.c.
Sequencing on the work branch
Recommended commit shape on refactor/s11-05-storage-cast-sweep:
refactor: S11.05 migrate StreamSender onto PoolAllocator — smallest of
the three public classes, exercises the shared NullSender fallback path
established by S11.04.
refactor: S11.05 migrate RecordStore onto PoolAllocator — TU-internal,
prep step for BlockStore. The 3-TU split applies to a non-public type for
the first time.
refactor: S11.05 migrate BlockStore onto PoolAllocator — consumes 3+4's
results; switches the BlockStore slot from embedded structs to pointers
into RecordStore and BlockSequence pools.
SolidSyslog<Class>Static.c — pool (<Class>_InUse[N], <Class>_Pool[N], <Class>_Allocator), fallback, _Create/_Destroy/IndexFromHandle/ CleanupAtIndex. NULL-config validation preserved where it existed
pre-migration.
Public header — delete SolidSyslog<Class>Storage typedef and SOLIDSYSLOG_<*>_STORAGE_SIZE enum. Drop the storage parameter from _Create. Adopt _Destroy(base) shape.
TunableSOLIDSYSLOG_<CLASS>_POOL_SIZE (default 1U) in Core/Interface/SolidSyslogTunablesDefaults.h with #ifndef override
#error floor.
Error messages_POOL_EXHAUSTED (ERROR) + _UNKNOWN_DESTROY
(WARNING) in Core/Source/SolidSyslogErrorMessages.h.
Core/Source/CMakeLists.txt — add the three new TU entries per class
(drop the old single-TU entry).
Existing test file — drop <*>Storage locals; update _Destroy(void)→ _Destroy(handle) call sites.
New Pool TEST_GROUP — FillingPoolThenOverflowReturnsDistinctFallback
at minimum.
CLAUDE.md audience-table row updated — drop Storage/SIZE mentions,
document the new _Destroy(base) shape.
<Class>.c — vtable-less; existing logic, _Init → _Initialise
rename, gain a _Cleanup.
<Class>Private.h — rename of current <Class>.h. Struct + _Initialise/ _Cleanup decls. Still TU-internal.
<Class>Static.c — pool sized directly to SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE. External-linkage _Create/_Destroy
so BlockStoreStatic.c can link. Class-private fallback (TU-internal nature
means there's no shared GoF null candidate).
Tests — <Class>Test.cpp updated to use the new _Create/_Destroy
shape. Existing tests carry forward as regression net. No new pool tests —
the 1:1 invariant with BlockStore is the contract; if either pool exhausts
before BlockStore's does, BlockStore's outer test for pool-exhaustion
would have failed first.
No public-header / tunable / audience-table changes — these classes
stay TU-internal.
Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_PosixTcp.c — drop the static SolidSyslogStreamSenderStorage senderStorage; line.
Acceptance criteria
All five classes follow the canonical 3-TU pattern. Per-class *Static.c at ≤ ~3 file-scope functions + CleanupAtIndex (+ NULL-config
validation where it existed pre-migration).
Public SolidSyslog<*>Storage types and SOLIDSYSLOG_<*>_STORAGE_SIZE
enums gone from Core/Interface/. Search proves zero remaining
references project-wide.
RecordStore and BlockSequence are TU-internal pool-allocated classes
sized to SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE. BlockStore's slot holds
pointers to them, not embedded structs.
Per-class Pool TEST_GROUP — at minimum FillingPoolThenOverflowReturnsDistinctFallback for the three public
classes. The TU-internal pair piggybacks on BlockStore's pool test for
exhaustion coverage (since their pools are sized 1:1).
cppcheck-misra produces no new findings vs running the same command on main. Track which storage-cast suppressions evaporate (record in
DEVLOG) — they are the MISRA bonus the epic promised.
Coverage at 100% line for every new and migrated file.
Validation step: per migrated class, re-run the full suite at SOLIDSYSLOG_<CLASS>_POOL_SIZE=3 via SOLIDSYSLOG_USER_TUNABLES_FILE
override. For BlockStore, bumping its tunable to 3 must also grow
RecordStore and BlockSequence pools to 3 automatically — confirms the
shared sizing.
Verify in cpputest-freertos container per feedback_verify_in_freertos_host_image.
DEVLOG entry on the work branch BEFORE the PR per feedback_devlog_in_pr.
File-scope statics use the class-prefix shape established in S11.04.
MISRA storage-cast deviation removal beyond the three migrated classes —
the wholesale sweep is S11.11; this story removes only the deviations that
apply to its three classes.
BlockStore algorithmic changes — the migration is mechanical, not
architectural. The split into BlockStore + RecordStore + BlockSequence is
preserved as-is; only their lifecycle and ownership change.
No widening cppcheck-misra to additional scopes — that's E10's territory.
Memory pointers for the next session
Read these before touching anything:
CLAUDE.md (project root) + SKILL.md
MEMORY.md (project memory index) — particularly:
project_e11_static_pool_design
project_e11_three_tu_split
project_pool_allocator_helper
project_configlock_injection
feedback_storage_pattern — storage convention; deletion is the point
feedback_bad_setup_contract — severity vocabulary for preserved
NULL-checks
feedback_discuss_production_changes — production-code changes need
explicit approval; behaviour-preserving refactor here, but any new
validation requires discussion
S11.04's DEVLOG entry — captures the bad-setup contract, file-scope static
naming, and NullSender drop-on-the-floor semantic inherited here.
Open questions for the next session to surface
Whether to bundle all five migrations into one PR or split.Default:
bundled — S11.04 set the precedent for sweep stories with multiple
per-class commits in one PR. Storage-type deletion is the cross-cutting
concern that benefits from one review surface.
MetaSd/OriginSd validation symmetry — TimeQualitySd grew NULL-callback
validation in S11.04 from CodeRabbit's nudge. MetaSd already validates;
OriginSd doesn't. Should a separate hardening story cover this, or roll it
into E12? Default: out of scope here — S11.05 is the storage-cast
sweep; validation symmetry belongs in a focused commit (or E12). Worth
explicit acknowledgement so it's not forgotten.
Naming for the TU-internal sub-components' file-scope statics. They
don't have a SolidSyslog prefix on their basename today
(RecordStore.c, not SolidSyslogRecordStore.c) — consistent with the
established convention for TU-internal classes. Their statics should
follow RecordStore_InUse etc., matching the basename — confirm in
session before naming the symbols.
Parent epic: #29
Context
S11.04 (#400) completed the Core sweep for the six file-scope-singleton classes
that had no public storage type. This story finishes the Core sweep by migrating
the three remaining storage-cast classes plus the two TU-internal
sub-components that BlockStore composes:
Public, storage-cast today — pool migration + public Storage-type deletion:
BlockStoreStoreBlockDevice, full-callbacks, threshold cb, security policyRecordStore+BlockSequence(today embedded in storage blob)FileBlockDeviceBlockDeviceFile*s, path prefix, cached open block indexStreamSenderSenderResolver,Stream,Endpoint,EndpointVersionSolidSyslogNullSendershipped in S11.04TU-internal sub-components of BlockStore — pool migration only (already
private in
Core/Source/; no public Storage type to delete):RecordStore_Init(self, securityPolicy)BlockSequence_Init(self, config)CircularBuffer(also storage-cast) was migrated in S11.02 (#396) as the pilotand is the reference for the public-class shape.
Goal
Apply the proven 3-TU split +
SolidSyslogPoolAllocatorpattern to all fiveclasses above. After this story:
STORAGE_SIZEenums for the three public classes —gone. Every caller drops its
<*>Storagelocal/static declaration._Createloses thestorageparameter on the three public classes._Destroy(handle)shape everywhere (S11.04 pattern).RecordStoreandBlockSequenceget the canonical 3-TU split too —_Init(self, ...)→_Create(...)+_Destroy(handle), vending pool slots.Their headers and TUs remain in
Core/Source/(TU-internal). BlockStore holdspointers to them, not embedded structs.
SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE— they are 1:1 with BlockStore byconstruction and don't merit their own integrator-facing tunable.
Per-class delta target unchanged from S11.02:
*Static.cends at ~3 file-scopefunctions (
_Create,_Destroy,IndexFromHandle) + theCleanupAtIndexbridge + per-class config validation where one exists. No new MISRA findings.
Decisions locked in pre-raise
1. Fallback objects reuse S11.04's shared GoF nulls where they fit.
BlockStoreSolidSyslogNullStore(already public,_Get-only after S11.04)FileBlockDeviceBlockDevicehas no shared GoF null andFileBlockDeviceis the only BlockDevice implementor todayStreamSenderSolidSyslogNullSender(shipped in S11.04)RecordStoreBlockSequenceThe TU-internal class-private fallbacks for RecordStore and BlockSequence can
be minimal: their
_Createreturning the fallback is observable only byBlockStoreStatic.c, which already handles pool-exhausted Create-time failure
by returning
SolidSyslogNullStore_Get()from its own outer Create.2. Pool size defaults.
BlockStoreSOLIDSYSLOG_BLOCK_STORE_POOL_SIZE1UFileBlockDeviceSOLIDSYSLOG_FILE_BLOCK_DEVICE_POOL_SIZE1UStreamSenderSOLIDSYSLOG_STREAM_SENDER_POOL_SIZE1URecordStoreSOLIDSYSLOG_BLOCK_STORE_POOL_SIZEdirectlyBlockSequenceSOLIDSYSLOG_BLOCK_STORE_POOL_SIZEdirectlyBy construction, every successful BlockStore Create consumes one RecordStore
slot and one BlockSequence slot from their respective pools. Bumping
SOLIDSYSLOG_BLOCK_STORE_POOL_SIZEautomatically grows all three together.RecordStore and BlockSequence emit a class-private POOL_EXHAUSTED message if
ever called more times than expected — they shouldn't be, but the guard
catches a future refactor that breaks the 1:1 invariant.
3. Three-TU split per class — unchanged from
project_e11_three_tu_split:SolidSyslog<Class>.c(or<Class>.cfor TU-internal) — vtable + private_Initialise/_Cleanup.SolidSyslog<Class>Private.h(or<Class>Private.h) — struct definition +_Initialise/_Cleanupdecls. TU-internal; tests must not include.SolidSyslog<Class>Static.c(or<Class>Static.c) — pool,_Create/_Destroy,IndexFromHandle,CleanupAtIndexbridge, fallbackassignment.
For the two TU-internal sub-components, the
<Class>Static.cexports<Class>_Createand<Class>_Destroywith external linkage soBlockStoreStatic.ccan link to them. There is noSolidSyslog<Class>-stylepublic header.
4. BlockStore's slot struct holds pointers, not embedded structs, after
this story:
BlockStore_Create calls RecordStore_Create and BlockSequence_Create to obtain
the handles, in that order. BlockStore_Cleanup calls their _Destroys in
reverse order. The 1:1 invariant means under normal operation neither inner
Create ever returns its fallback; if either does, BlockStore_Create routes to
SolidSyslogNullStore_Get()and emits its own POOL_EXHAUSTED error.5. No new MISRA suppressions. E11 invariant. Storage-cast MISRA deviations
get removed by this story (the whole point) — track which deviations
evaporate.
6. File-scope statics follow the NAMING.md sweep landed in S11.04 —
<Class>_InUse,<Class>_Pool,<Class>_Allocatorfrom the start. No bareInUse/Pool/Allocatorin any new*Static.c.Sequencing on the work branch
Recommended commit shape on
refactor/s11-05-storage-cast-sweep:refactor: S11.05 migrate StreamSender onto PoolAllocator— smallest ofthe three public classes, exercises the shared
NullSenderfallback pathestablished by S11.04.
refactor: S11.05 migrate FileBlockDevice onto PoolAllocator—class-private fallback.
refactor: S11.05 migrate RecordStore onto PoolAllocator— TU-internal,prep step for BlockStore. The 3-TU split applies to a non-public type for
the first time.
refactor: S11.05 migrate BlockSequence onto PoolAllocator— TU-internal,prep step for BlockStore.
refactor: S11.05 migrate BlockStore onto PoolAllocator— consumes 3+4'sresults; switches the BlockStore slot from embedded structs to pointers
into RecordStore and BlockSequence pools.
docs: update DEVLOG for S11.05.Squash-merge title:
feat: S11.05 Core storage-cast classes onto PoolAllocator.Per-class scope
For each migrating class, the per-class commit lands:
Public, storage-cast classes (StreamSender, FileBlockDevice, BlockStore):
SolidSyslog<Class>.c— extracted vtable methods +_Initialise(base, ...)_Cleanup(base). Cast uses existingSelfFromBase.SolidSyslog<Class>Private.h— TU-internal. Struct definition +_Initialise/_Cleanupdecls.SolidSyslog<Class>Static.c— pool (<Class>_InUse[N],<Class>_Pool[N],<Class>_Allocator), fallback,_Create/_Destroy/IndexFromHandle/CleanupAtIndex. NULL-config validation preserved where it existedpre-migration.
SolidSyslog<Class>Storagetypedef andSOLIDSYSLOG_<*>_STORAGE_SIZEenum. Drop thestorageparameter from_Create. Adopt_Destroy(base)shape.SOLIDSYSLOG_<CLASS>_POOL_SIZE(default 1U) inCore/Interface/SolidSyslogTunablesDefaults.hwith#ifndefoverride#errorfloor._POOL_EXHAUSTED(ERROR) +_UNKNOWN_DESTROY(WARNING) in
Core/Source/SolidSyslogErrorMessages.h.Core/Source/CMakeLists.txt— add the three new TU entries per class(drop the old single-TU entry).
<*>Storagelocals; update_Destroy(void)→_Destroy(handle)call sites.FillingPoolThenOverflowReturnsDistinctFallbackat minimum.
CLAUDE.mdaudience-table row updated — drop Storage/SIZE mentions,document the new
_Destroy(base)shape.TU-internal sub-components (RecordStore, BlockSequence):
<Class>.c— vtable-less; existing logic,_Init→_Initialiserename, gain a
_Cleanup.<Class>Private.h— rename of current<Class>.h. Struct +_Initialise/_Cleanupdecls. Still TU-internal.<Class>Static.c— pool sized directly toSOLIDSYSLOG_BLOCK_STORE_POOL_SIZE. External-linkage_Create/_Destroyso BlockStoreStatic.c can link. Class-private fallback (TU-internal nature
means there's no shared GoF null candidate).
<Class>Test.cppupdated to use the new_Create/_Destroyshape. Existing tests carry forward as regression net. No new pool tests —
the 1:1 invariant with BlockStore is the contract; if either pool exhausts
before BlockStore's does, BlockStore's outer test for pool-exhaustion
would have failed first.
stay TU-internal.
Public API changes
RecordStore and BlockSequence internal API:
Caller updates land in the per-class migration commit:
Tests/SolidSyslog<Class>Test.cpp,BlockStoreDrainOrderingTest.cpp,BlockStorePosixTest.cpp,BlockSequenceTest.cpp— drop storage locals,update Init→Create signatures.
Bdd/Targets/Linux/main.c,Bdd/Targets/Windows/BddTargetWindows.c,Bdd/Targets/FreeRtos/main.c— dropstatic <*>Storagelines; update_Createcall sites.Bdd/Targets/Common/BddTargetTlsSender_OpenSsl_PosixTcp.c— drop thestatic SolidSyslogStreamSenderStorage senderStorage;line.Acceptance criteria
*Static.cat ≤ ~3 file-scope functions +CleanupAtIndex(+ NULL-configvalidation where it existed pre-migration).
SolidSyslog<*>Storagetypes andSOLIDSYSLOG_<*>_STORAGE_SIZEenums gone from
Core/Interface/. Search proves zero remainingreferences project-wide.
sized to
SOLIDSYSLOG_BLOCK_STORE_POOL_SIZE. BlockStore's slot holdspointers to them, not embedded structs.
FillingPoolThenOverflowReturnsDistinctFallbackfor the three publicclasses. The TU-internal pair piggybacks on BlockStore's pool test for
exhaustion coverage (since their pools are sized 1:1).
clang-format, iwyu.
main. Track which storage-cast suppressions evaporate (record inDEVLOG) — they are the MISRA bonus the epic promised.
SOLIDSYSLOG_<CLASS>_POOL_SIZE=3viaSOLIDSYSLOG_USER_TUNABLES_FILEoverride. For BlockStore, bumping its tunable to 3 must also grow
RecordStore and BlockSequence pools to 3 automatically — confirms the
shared sizing.
cpputest-freertoscontainer perfeedback_verify_in_freertos_host_image.feedback_devlog_in_pr.Out of scope (this story will not touch)
S11.06–S11.09.
SolidSyslogcore retrofit — S11.10.the wholesale sweep is S11.11; this story removes only the deviations that
apply to its three classes.
BlockStorealgorithmic changes — the migration is mechanical, notarchitectural. The split into BlockStore + RecordStore + BlockSequence is
preserved as-is; only their lifecycle and ownership change.
Memory pointers for the next session
Read these before touching anything:
CLAUDE.md(project root) +SKILL.mdMEMORY.md(project memory index) — particularly:project_e11_static_pool_designproject_e11_three_tu_splitproject_pool_allocator_helperproject_configlock_injectionfeedback_storage_pattern— storage convention; deletion is the pointfeedback_bad_setup_contract— severity vocabulary for preservedNULL-checks
feedback_discuss_production_changes— production-code changes needexplicit approval; behaviour-preserving refactor here, but any new
validation requires discussion
feedback_tdd_and_review_stylefeedback_cppcheck_misra_invariantfeedback_devlog_in_prfeedback_pr_templatefeedback_no_git_commit_dash_sfeedback_no_mergefeedback_verify_in_freertos_host_imagenaming, and NullSender drop-on-the-floor semantic inherited here.
Open questions for the next session to surface
bundled — S11.04 set the precedent for sweep stories with multiple
per-class commits in one PR. Storage-type deletion is the cross-cutting
concern that benefits from one review surface.
validation in S11.04 from CodeRabbit's nudge. MetaSd already validates;
OriginSd doesn't. Should a separate hardening story cover this, or roll it
into E12? Default: out of scope here — S11.05 is the storage-cast
sweep; validation symmetry belongs in a focused commit (or E12). Worth
explicit acknowledgement so it's not forgotten.
don't have a
SolidSyslogprefix on their basename today(
RecordStore.c, notSolidSyslogRecordStore.c) — consistent with theestablished convention for TU-internal classes. Their statics should
follow
RecordStore_InUseetc., matching the basename — confirm insession before naming the symbols.