feat(model): add code conformance review to issue lifecycle #82
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "stack72/code-conformance-review"
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?
Summary
Test plan
Code Review
Blocking Issues
None.
Suggestions
code-conformance-clearcheck gatescompletefrompr_openandreleasingbutcode_conformance_reviewcan't be called from those phases. The checkappliesTo: ["link_pr", "complete"]fires for all three source phases ofcomplete(implementing,pr_open,releasing). For new lifecycles this is fine — by the time you reachpr_openorreleasing, the conformance review exists becauselink_pralready required it. But for any pre-existing lifecycle already inreleasingwhen this version is deployed, callingcompletewill fail ("No code conformance review exists"), and there is no self-healing path:code_conformance_reviewis only valid fromimplementingorpr_failed(perTRANSITIONS), so you can't retroactively satisfy the check fromreleasing. Users would need to callshipinstead ofcomplete, but the existing documentation and SKILL.md both saycomplete still works from implementing, pr_open, or releasing. Consider narrowing theappliesToto excludecompletefrompr_open/releasingphases (i.e., only gatecompletewhen the source isimplementing), or add an operational note to the upgrade description.All
code-conformance-cleartests usemethodName: "link_pr"; none exercisecomplete. The check logic doesn't branch on method name so behavior is identical, but having at least one test withmethodName: "complete"would make the coverage match the declaredappliesToand provide executable documentation of the intent.start: persists author in context-maintest sendsauthorUsernamein the API response body, butissue_lifecycle.tswritesauthor: issue.author. WhetherfetchIssue()mapsauthorUsername → authordepends on_lib/swamp_club.ts(not changed in this PR). If that mapping isn't there, the test passes for the wrong reason (the context write would recordauthor: undefinedrather than"external-user"). Worth verifying — though this is pre-existing behaviour, not introduced here.Adversarial Review
Medium
issue-lifecycle/extensions/models/issue_lifecycle.ts:1373-1376 - justify_deviations silently drops empty-string justifications but reports them as justified
The justify_deviations method uses a truthy check to decide whether to apply a justification to a step. An empty string is falsy in JavaScript, so justificationMap.get(s.order) returning an empty string causes the branch to be skipped and the step is left unchanged. However, the justified counter uses args.justifications.length, which counts the empty-string entry as successfully justified.
Breaking input: justify_deviations with justifications containing order 2 and an empty-string justification.
What happens:
Suggested fix: Add .min(1) to the justification field in the justify_deviations arguments schema (line 1331). This rejects empty strings at the schema boundary.
Low
issue-lifecycle/extensions/models/issue_lifecycle.ts:1373-1379 - justify_deviations silently ignores justifications for non-existent step orders
If a justification targets an order number that does not appear in the existing review steps array, the justification is silently discarded. But justified = args.justifications.length still counts it, producing a misleading log.
Breaking input: Review has steps with orders [1, 2]. Call justify_deviations with order 99.
What happens: Log says Justified 1 deviation(s) but nothing changed. The remaining count is correct, so the inconsistency is visible but confusing.
Suggested fix: Count how many justifications actually matched instead of using args.justifications.length. Optionally warn about unmatched orders.
This is LOW because it only triggers on agent error (providing wrong order numbers) and the remaining count self-corrects.
issue-lifecycle/extensions/models/_lib/schemas.ts:209 - StepVerificationSchema.justification allows empty strings
The justification field on StepVerificationSchema is z.string().optional() so when provided it can be empty. The code-conformance-clear check uses !s.justification to test for unjustified steps, so empty string is treated as unjustified (correct behavior). However, code_conformance_review would accept and persist an empty justification on a step. This is cosmetically inconsistent but functionally correct since the check handles it properly.
Verdict
PASS - The code is well-structured, follows existing patterns in the codebase (resource/check/method lifecycle), and has solid test coverage across happy paths, error cases, and the check gate. The medium finding (empty-string justification) is unlikely in practice and the check still blocks correctly - it is a UX/logging issue, not a data integrity one. No critical or high severity issues found.
Code Review
Blocking Issues
None.
Suggestions
Upgrade description understates the scope of
code-conformance-clear(extensions/models/issue_lifecycle.ts:149–157): The version upgrade description says "Gates link_pr" but the check also gatescomplete(viaappliesTo: ["link_pr", "complete"]). This means callingcompletedirectly fromimplementingnow requires a conformance review where it previously didn't. The design is correct and intentional, but the upgrade note is misleading — worth clarifying as "Gates link_pr and complete" to set accurate expectations for operators applying the upgrade.Missing test case:
code-conformance-clearwhen no plan exists (extensions/models/issue_lifecycle_test.ts): The check has a"No plan exists."error path (issue_lifecycle.ts:398–400) but no test covers it. The four existing test cases (passes, no review, stale version, unjustified deviations) are good, but this fifth path goes untested. Low priority since bothlink_prandcompleterequire an earlierplancall in the normal flow, but it's a reachable code path.buildTestContextreturn type is pinned tostartcontext (issue_lifecycle_test.ts:42):contextis typed asParameters<typeof model.methods.pr_merged.execute>[1], which works for tests but may confuse readers since the helper is used for many other methods. A union or a hand-written interface would be clearer. No functional impact.Adversarial Review
Critical / High
None found.
Medium
justify_deviations reports misleading count for non-existent step orders
File:
extensions/models/issue_lifecycle.ts:1393-1395The
justifiedcount comes fromargs.justifications.length(inputsprovided), not from the number of justifications actually applied. If a
caller passes an order that does not exist in the review (e.g., order 999
when only steps 1 and 2 exist), the map lookup silently misses, no step is
updated, but the log says "Justified 1 deviation(s)". The
remainingcountis correct (computed from
updatedSteps), so thecode-conformance-cleargate still works -- but the feedback to the caller is wrong.
Breaking example: Call
justify_deviationswith order 999 and ajustification when the review has steps 1 and 2. Log says "Justified 1
deviation(s), 1 unjustified remain" but nothing was actually justified.
Suggested fix: Count applied justifications instead of input length. Also
use
justification !== undefinedinstead of the truthy check, which wouldsilently drop empty-string justifications. (The analogous pattern exists in
resolve_findingsfor finding IDs, but that is pre-existing code.)Low
Duplicate order values in justifications array silently lose data
File:
extensions/models/issue_lifecycle.ts:1369-1371The
justificationMapis built withnew Map(...). If two entries sharethe same
order, only the last one wins -- the earlier justification issilently discarded. The Zod schema does not enforce unique orders.
Impact: Unlikely in practice since the LLM agent generates these
programmatically, but the API contract does not prevent it.
Documentation gap: complete as quick close-out now requires review
File:
implementation.md:135-136The
code-conformance-clearcheck applies to bothlink_prandcomplete. The documentation sayscompletestill works fromimplementing, pr_open, or releasing for quick close-out, but does not
mention that it now requires a conformance review first. Callers following
the documented quick close-out path will hit an unexpected gate failure.
Impact: Confusing error for agents following the documented path, not a
correctness bug. The gate is arguably the right behavior, but the docs
should say so.
Verdict
PASS -- The core logic is sound. The new code_conformance_review method,
justify_deviations method, code-conformance-clear check, StepVerificationSchema,
and CodeConformanceReviewSchema are correctly wired into the state machine. The
gate check properly validates plan version alignment and unjustified deviations.
Tests cover the key paths (happy path, missing plan, missing review, stale
version, unjustified deviations). Root and issue-lifecycle/ copies are verified
identical. The medium finding about misleading justification counts is a logging
issue, not a correctness bug -- the actual gate enforcement works correctly.