Two contracts libs owns are enforced by copies living in downstream repos, and both sets of copies have drifted.
1. The static readonly title guard — the copies now enforce different rules
@workglow/task-graph requires every task class to declare static readonly title (the CLI progress UI labels rows with it). libs ships no assertion for it, so two downstreams wrote their own regex scanner:
| file |
regex |
workglow-dev/sec → src/task/taskTitles.test.ts:66 |
/^\s*static (?:readonly )?title(?::\s*\w+)? = "([^"]*)"/ |
sroussey/embarc-data → src/taskTitles.test.ts:26 |
/^\s*(?:(?:public|protected|private)\s+)?static\s+(?:override\s+)?(?:readonly\s+)?title(?:\s*:\s*\w+)?\s*=\s*"([^"]*)"/ |
embarc's copy grew a documented fix that sec's did not receive — "a class extending a base that already declares title writes public static override title, and reading that as 'no title' would report a titled task as untitled." So sec's guard silently reads public static override title as "no title", and the two now disagree about what satisfies one libs convention. On 08-24 they differed only in header and path roots; this is a behavioural divergence.
Ask: export the assertion from @workglow/task-graph (e.g. assertEveryTaskDeclaresTitle(dir) or, better, a registry-walking check that needs no regex at all — TaskRegistry already holds the constructors), and let both downstreams delete their scanners.
2. sourceStubs.ts — two copies, drifted by the same six lines for a third cycle
md5 d17ea5c005079fc7f66c1202050ad4ca libs/scripts/lib/sourceStubs.ts (197 lines)
md5 a52fa2fff04c19eeed06e1ff248ffbe7 sec/scripts/sourceStubs.ts (191 lines)
The delta is exactly the doc block libs added at fea0c8151 explaining why type stubs break under TypeScript project references. Both carry the @workglow-source-stub sentinel that scripts/publish-workspaces.ts:87-96 and sec/scripts/checkPackedContents.ts refuse a publish on — i.e. a shared invariant implemented twice. Nothing tests that they agree.
Ask: publish the module (a @workglow/dev-scripts entry, or a subpath off an existing package) and have sec import it; failing that, a test in either repo asserting the two files are identical.
Why this is worth doing now
The consolidation pattern is established and has worked twice in two windows:
afef1c109 moved the whole workglow binary body into a published runWorkglowCli, and workglow.ts became two statements; sec and embarc both call it rather than copying it.
- This window,
@workglow/sec exported defineStorage and embarc deleted 86 lines of its own definer to take it — naming the drift its copy had accumulated in the commit message, with sec adding storageRegistry.portability.test.ts to pin the shape upstream.
These two guards are the same shape and much smaller. Adjacent leftovers on the same list, for context rather than as asks here: four bun link scripts (72 / 180 / 175 / 228 LOC), two scoreExtraction implementations (#861), and the 105 MB s1 fixture corpus duplicated between sec and embarc with no --check (sroussey/embarc-data#68, cycle 6).
Found during the 2026-08-31 review. Snapshot: workglow-dev/prd → analysis/grades/2026-08-31/cross-repo-integration.md.
Two contracts libs owns are enforced by copies living in downstream repos, and both sets of copies have drifted.
1. The
static readonly titleguard — the copies now enforce different rules@workglow/task-graphrequires every task class to declarestatic readonly title(the CLI progress UI labels rows with it). libs ships no assertion for it, so two downstreams wrote their own regex scanner:workglow-dev/sec→src/task/taskTitles.test.ts:66/^\s*static (?:readonly )?title(?::\s*\w+)? = "([^"]*)"/sroussey/embarc-data→src/taskTitles.test.ts:26/^\s*(?:(?:public|protected|private)\s+)?static\s+(?:override\s+)?(?:readonly\s+)?title(?:\s*:\s*\w+)?\s*=\s*"([^"]*)"/embarc's copy grew a documented fix that sec's did not receive — "a class extending a base that already declares
titlewritespublic static override title, and reading that as 'no title' would report a titled task as untitled." So sec's guard silently readspublic static override titleas "no title", and the two now disagree about what satisfies one libs convention. On 08-24 they differed only in header and path roots; this is a behavioural divergence.Ask: export the assertion from
@workglow/task-graph(e.g.assertEveryTaskDeclaresTitle(dir)or, better, a registry-walking check that needs no regex at all —TaskRegistryalready holds the constructors), and let both downstreams delete their scanners.2.
sourceStubs.ts— two copies, drifted by the same six lines for a third cycleThe delta is exactly the doc block libs added at
fea0c8151explaining why type stubs break under TypeScript project references. Both carry the@workglow-source-stubsentinel thatscripts/publish-workspaces.ts:87-96andsec/scripts/checkPackedContents.tsrefuse a publish on — i.e. a shared invariant implemented twice. Nothing tests that they agree.Ask: publish the module (a
@workglow/dev-scriptsentry, or a subpath off an existing package) and have sec import it; failing that, a test in either repo asserting the two files are identical.Why this is worth doing now
The consolidation pattern is established and has worked twice in two windows:
afef1c109moved the wholeworkglowbinary body into a publishedrunWorkglowCli, andworkglow.tsbecame two statements; sec and embarc both call it rather than copying it.@workglow/secexporteddefineStorageand embarc deleted 86 lines of its own definer to take it — naming the drift its copy had accumulated in the commit message, with sec addingstorageRegistry.portability.test.tsto pin the shape upstream.These two guards are the same shape and much smaller. Adjacent leftovers on the same list, for context rather than as asks here: four
bun linkscripts (72 / 180 / 175 / 228 LOC), twoscoreExtractionimplementations (#861), and the 105 MB s1 fixture corpus duplicated between sec and embarc with no--check(sroussey/embarc-data#68, cycle 6).Found during the 2026-08-31 review. Snapshot:
workglow-dev/prd→analysis/grades/2026-08-31/cross-repo-integration.md.