Skip to content

Commit 5c1e1c4

Browse files
PhysShellclaude
andcommitted
feat(S0-A): additive --fix-candidates extractor metadata for subscription fixes
Part A of S0 (`own-fix subscriptions candidates`). Roslyn extractor only; the Python collector is Part B. Analysis-only, strictly additive, arbiter contract. New internal flag `--fix-candidates`. With it OFF the facts JSON is byte-for-byte identical (verified: flag-off vs pre-S0 `git stash` build diff clean) and `ownir_version` is unchanged (0). With it ON: - top-level `fix_candidates_version: 1` (additive metadata, NOT an ownir_version bump — no new resource-kind or analysis-routing value); - each component gains `qualified_name` (one documented FQN format) + type-shape metadata (`is_partial`, `is_nested`, `declaration_count`, `is_generated`); - each eligible non-timer `+=` subscription gains a namespaced `fix` block. The `fix` block honours the locked amendments: - **Full TextSpan** (`start`/`length` UTF-16 + start/end line/column), not a bare line — a future rewriter needs the absolute span; two subs on one line differ. - **Semantic INotifyPropertyChanged**, never name-matching: `event_contract` is `inotify_property_changed` only for the actual `INotifyPropertyChanged.PropertyChanged` member or a proven implementation; a same-named event on an unrelated type is `name_only`; anything else `other`. - **Symbol-based teardown**, separate from the existing string-keyed `released` (which is untouched): `teardown.status` is `none` / `exact` (one `-=` site with equal event symbol + receiver identity + handler identity) / `ambiguous` (multiple sites, or any identity unresolved); each candidate carries its span. - **Line-independent identity constituents** emitted for Part B to hash: `enclosing_member`, `event_identity`, `source_identity`, `handler_identity` (method symbol, else a normalized syntax fingerprint), `occurrence_ordinal`. - **Nested-type boundary**: a `fix` block is attached only when the acquire's IMMEDIATE containing type is this class (a nested class's subscription is fixed under the nested type's own iteration, never the outer's). Existing subscription facts (and `released`) are unchanged. Sample `FixCandidatesSample.cs` + `tests/check_fix_candidates_facts.py` cover: exact/none/ambiguous teardown, INPC vs name_only vs other, two-subs-one-line distinct spans, wrapped-delegate normalization (`+= new H(m)` / `-= m` = exact), nested isolation, and flag-off additive-cleanliness. CI step added; ruff + run_tests green locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JxKjqdGEFzq4UzZupw379G
1 parent ff21d4a commit 5c1e1c4

4 files changed

Lines changed: 570 additions & 17 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1608,6 +1608,26 @@ jobs:
16081608
fi
16091609
echo "OK: declared weak-subscribe wrapper = accepted release; += unaffected; own.toml plumbed; malformed config is a hard error"
16101610
1611+
# S0 (--fix-candidates, Part A): ADDITIVE fix-candidate metadata — a
1612+
# namespaced `fix` block on each `+=` subscription, component
1613+
# `qualified_name`/shape, and a top-level `fix_candidates_version`. With the
1614+
# flag OFF the facts are byte-identical (the extractor fact tests above run
1615+
# flag-off and would break otherwise); this step asserts the metadata is
1616+
# correct and that the off-run leaks none of it.
1617+
- name: S0 fix-candidates — extractor metadata (Part A)
1618+
run: |
1619+
fc=frontend/roslyn/samples/FixCandidatesSample.cs
1620+
dotnet run --project frontend/roslyn/OwnSharp.Extractor -- \
1621+
"$fc" --fix-candidates -o "$RUNNER_TEMP/fc_on.json"
1622+
dotnet run --project frontend/roslyn/OwnSharp.Extractor -- \
1623+
"$fc" -o "$RUNNER_TEMP/fc_off.json"
1624+
python tests/check_fix_candidates_facts.py "$RUNNER_TEMP/fc_on.json" "$RUNNER_TEMP/fc_off.json"
1625+
# Byte-level additivity: the flag-off output must contain NONE of the new keys.
1626+
if grep -qE '"(fix|qualified_name|fix_candidates_version)"' "$RUNNER_TEMP/fc_off.json"; then
1627+
echo "FAIL: flag-off output leaked fix-candidate metadata"; exit 1
1628+
fi
1629+
echo "OK: fix-candidate metadata correct; flag-off is additive-clean"
1630+
16111631
# The OwnTS frontend spike (P-020 Own.React): the SAME OwnIR seam, fed from a
16121632
# React .tsx instead of C#. Two analyses over the one core: (1) a useEffect
16131633
# acquire (timer / subscribe / listener) with no cleanup return is the core's

0 commit comments

Comments
 (0)