Generator: input/output fields not declared as class properties + bare helper calls + duration/implies literal rewriter gaps #50
Labels
No labels
blocked
bug
design
docs
engineering
feature
infra
priority:high
priority:low
priority:medium
question
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: sscoble/speckl#50
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during verification of sscoble/speckl#47 (B2.1 constraint forwarding, now passing). The B2.1 fix is in and working — constraint expressions are rewritten correctly to
this.<field> <op> ...form, and a runtime test onAccessControl.checkConstraint10()withrateLimitRemaining = -1correctly returnsfalse. The original "Cannot find name: userId, maxDose, sessionValid, …" regression is fully resolved across all 5 constraint examples (AccessControl, DrugDosageCalculator, PaymentService, RetryHandler, SQLiteWAL).However, four separate generator gaps surfaced that block clean
tsc --noEmiton those same five examples. They are out of scope for #47 (which is the constraint-forwarding design) and warrant their own ticket.Issues
1. Input/output fields not declared as class properties
AccessControlMachinereferencesthis.userId,this.rateLimitRemaining, etc. incheckConstraintN()methods, but the class body does not declare these fields. The methods pass through TS becausethis.xis structurally valid TS, but the class shape is wrong:Repro: generate any of the five constraint examples, run
tsc --noEmit. Affects all 5.Fix: in
compiler/src/generators/typescript-state-machine.ts, when emitting the class body, declare each input/output field as a class property using the sametsType(...)mapping used for state vars.2. Bare
count()andimplies()calls in constraint methodsConstraints like
count(userRoles) >= 0andimplies(allowed, policyId != "")are emitted with bare function calls:Neither
countnorimpliesis imported or declared in the generated module:Fix: either (a) inline-rewrite
count(x)to(x as any[]).length/x.lengthandimplies(p, q)to(!p) || q, or (b) emit small helper functions at the top of the generated module.3. Duration literals not translated
RetryHandler.speckhasconstraint: totalDelay <= 30s. The rewriter emitsthis.totalDelay <= 30s, but30sis SpeckDL DSL syntax for a duration (presumably 30 seconds = 30000 ms), not a valid TS numeric literal:Repro:
examples/RetryHandler.speck, line 69:constraint: totalDelay <= 30s.Fix: detect the
Nd/Ns/Nmsduration suffix in the expression rewriter and emitN * unitMs(or whatever the canonical form is per SpeckDL semantics).4. Infix
x implies ynot recognizedSQLiteWAL.speckuseswalResetNeeded implies (nReaders == 0). The rewriter doesn't recognizeimpliesas a binary infix operator and emitsthis.walResetNeeded implies (this.nReaders === 0):The function form (
implies(x, y)) works fine (see AccessControl C8/C9/C12 which compile).Repro:
examples/SQLiteWAL.speck, line 88:constraint: walResetNeeded implies (nReaders == 0).Fix: in the rewriter, treat
impliesas a binary infix operator (translates to!a || b), the same wayand/or/notare already handled.Suggested scope
Single ticket covering all four (they're all in
compiler/src/generators/typescript-state-machine.tsand share the same rewriter/emit pipeline). After this lands, the same five constraint examples should compile clean undertsc --noEmitwith no new errors.Verification harness (from #47 dispatch)
Currently produces 6 errors (2 in RetryHandler, 4 in SQLiteWAL). After the four fixes above, should be 0.
Dispatch: agent-engineering — 2026-06-03 21:18 UTC (CoS cron)
Ticket: sscoble/speckl#50 ([speckl#37 follow-up] Generator: input/output fields not declared as class properties + bare helper calls + duration/implies literal rewriter gaps)
Context: This is the residual work from B2.1 (sscoble/speckl#47, closed 2026-06-03 03:47 UTC). After B2.1's parseInputBlock fix unblocked enough
tscto see further issues, four separate generator gaps surfaced that block cleantsc --noEmiton the same five constraint examples (AccessControl, DrugDosageCalculator, PaymentService, RetryHandler, SQLiteWAL). They are out of scope for #47 and warrant their own ticket.Scope: All four fixes are in
compiler/src/generators/typescript-state-machine.tsand share the same rewriter/emit pipeline. Single ticket, single PR acceptable.Acceptance Criteria (4 fixes):
tsType(...)mapping used for state vars.count(x)tox.length(or array cast) andimplies(p, q)to(!p) || q. OR emit small helper functions.Nd/Ns/Nmsduration suffix in the expression rewriter and emitN * unitMs.impliesas a binary infix operator (translates to!a || b) the same wayand/or/notare handled.Verification harness (from #47):
Should produce 0 errors after all four fixes (currently 6: 2 in RetryHandler, 4 in SQLiteWAL).
Out of scope: No
nix build, no PR, do not touch Z3 generator, do not touch parser, do not refactor surrounding emit pipeline.Branch:
fix/speckl-50-generator-gapsSubagent budget: 1200s wall-clock (one-shot, not parallel). Subagent should return a verdict and the diff (working tree changes). CoS will verify, commit, and close.
Assigned to: agent-engineering
Dispatch result (2026-06-03 21:18 → 21:24 UTC): BLOCKED — subagent max_iterations, work not started
Subagent verdict: agent-engineering hit max_iterations (50 API calls, 335s wall-clock) before any of the four fixes were attempted. The subagent's investigation surfaced a critical precondition failure that prevents any work on #50 from landing.
Critical finding: B2.1 baseline is missing from
mainThe subagent's verification of the current state:
compiler/src/generators/typescript-state-machine.ts(source)return rewriteExpr(c.expr, ...);return true;with// TODO: Evaluate in Z3 or runtimecompiler/src/parser.tsparseMemberBlock(lines 349+)input:/output:multi-lineinput: { ... }returns{ type: 'input', fields: [...] }{ type: 'input', typeExpr: { type: 'ident', name: '{' } }— parses{as identifierThis means sscoble/speckl#47 (B2.1) and sscoble/speckl#49 (#49) were closed prematurely — the prior dispatch cycle verified the work in a subagent's working tree but the changes were never committed to git. Subsequent working-tree resets (5
git resetoperations on branchfix/speckl-49-input-output-record-parserbetween 2026-06-02 15:36 and 21:25) destroyed the uncommitted work.Branch
fix/speckl-49-input-output-record-parserstill exists as a ref but points at the same SHA asmain(39823c4) with zero unique commits and zero diff. The branch is an empty pointer.Why #50 cannot proceed
count()/implies()rewriterreturn truewithout B2.1. The 6-error baseline assumes B2.1 is in.30s→30000)impliesoperator3 of 4 fixes are independent rewriter changes that could land without B2.1, but they cannot be verified without B2.1 because the constraint bodies that exercise them don't exist in the current pipeline.
Recommended next action (filed separately as sscoble/speckl#51)
Re-apply B2.1 + #49 work as a single coherent dispatch:
fix/speckl-51-reapply-b2.1-and-49CoS process improvement filed
This is the second documented case in 36 hours where the CoS closed a ticket based on a subagent's working-tree verification without committing the changes. The next cycle should add a
git diff --stat→ if non-empty → commit step before the HTTP PATCH close.Disposition
blockedlabel to speckl#50.CoS dispatch cycle 2026-06-03 21:24 UTC. (verdict 0/4 — total subagent budget consumed by investigation, no fix work.)
→ Follow-up tracked in sscoble/speckl#51 ([CRITICAL — Re-apply lost work] B2.1 constraint forwarding + #49 multi-line input/output parser). After #51 lands and is committed, re-dispatch #50 with the baseline in place.