Skip to content

Commit 377a7b1

Browse files
committed
feat(owen): public facade rebrand for the CLI, Action, and SARIF identity
Public product identity is "Owen" going forward - a package published as "OwnSharp.Cli" with the permanent command "ownsharp" would have locked the public identity to a single-language framing the project doesn't actually have (Own.NET is language-neutral at the OwnIR/core level and may ship a TypeScript frontend later). This is a public facade rebrand, not an internal refactor - see docs/notes/owen-public-facade.md for the full account of what changed and, just as deliberately, what did not. Public identity implemented: - NuGet package ID: OwnSharp.Cli -> Owen.Cli (confirmed unclaimed on nuget.org at time of writing via the v3 flat-container API) - Tool command: ownsharp -> owen - Action display name: "Own.NET resource-leak check" -> "Owen lifetime/resource check"; default SARIF filename own-net.sarif -> owen.sarif - SARIF tool.driver.name: "Own.NET" -> "Owen" (ownlang/ownir.py + ownlang/diag_sarif.py - the one place both the C#-facts and the .own-diagnostics SARIF paths share) - Cache directory: ~/.ownsharp/core/<version>/ -> ~/.owen/core/<version>/, with a simple fallback read (not a migration subsystem) from the previous location if a matching version was already unpacked there - Env var: OWEN_PYTHON preferred; legacy OWN_PYTHON still works as a temporary fallback and prints a deprecation note when it's the one actually used Kept unchanged (internal names, not mass-renamed): the OwnSharp.Cli project/namespace, OwnSharp.Extractor (project + its real ownsharp-extract.dll output filename), ownlang (package/module names, CLI, PYTHONPATH conventions), OwnIR, every OWN0xx diagnostic code, the Rust crates, every frontend/* directory name, historical docs/issues, the GitHub repo name, and scripts/own-check.sh (the Action still calls it internally). AssemblyName in the csproj stays "ownsharp" - it names the internal dll the owen shim launches, never seen by a user, so renaming it adds no user value. CLI contract additions: - `owen --help` is language-neutral at the product level while explicitly listing the one frontend actually wired in today ("Included frontend: .NET / C# (.cs, .csproj, .sln)") - no speculative TypeScript claim, no plugin framework added. - Unsupported input (e.g. a .ts file, or a directory with no .cs files) now fails explicitly with a new exit code 4 and an explicit message, instead of silently printing "0 findings" - indistinguishable from a genuinely clean C# scan before this change. CheckCommand.HasSupportedInput checks this before the extractor runs. Tests: ci.yml's ownsharp-cli-smoke job (key kept; only content changed) now pins package ID, command name, --help framing, --version, the unknown-command "owen:" prefix, a leak sample and a clean negative control both run from an installed checkout-free location, the Owen SARIF driver name, the exit-4 unsupported-input path (asserting the literal clean-scan phrase "0 findings." does NOT appear), OWEN_PYTHON's not-found path, and the legacy OWN_PYTHON fallback + its deprecation note - on both ubuntu-latest and windows-latest. Every one of these assertions was manually replicated and verified on this machine before being written into the workflow. Two other pre-existing SARIF driver-name assertions (ci.yml's own-check-codescan structural validation, tests/test_diag_sarif.py, tests/test_ownir.py) were updated to match "Owen" - caught by python tests/run_tests.py failing before the fix, not scope creep. PR separation: this is PR 1 of 3. Phase 3 (NuGet release pipeline, PR #244) and Phase 4 (Marketplace preparation, PR #245) both need to be rebuilt against this PR once merged, targeting Owen.Cli/owen and the Owen display name instead of the pre-rebrand identity their first drafts used. No oracle remeasurement or analyzer-semantics change is mixed into this PR.
1 parent 4c5a86b commit 377a7b1

17 files changed

Lines changed: 519 additions & 130 deletions

‎.github/workflows/ci.yml‎

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,15 +1717,15 @@ jobs:
17171717
run: |
17181718
# The contract GitHub's code-scanning ingest enforces, checked locally so
17191719
# the upload (own-check-codescan job) is never the first place a drift is
1720-
# found: a single-run 2.1.0 log, the Own.NET driver, and every result
1720+
# found: a single-run 2.1.0 log, the Owen driver, and every result
17211721
# carrying a catalogue ruleId + a located file. No upload, no permissions.
17221722
out="$RUNNER_TEMP/own.sarif"
17231723
scripts/own-check.sh --format sarif --severity warning -- frontend/roslyn/samples > "$out"
17241724
echo "wrote $(wc -c < "$out") bytes"
17251725
jq -e '.version == "2.1.0" and ((.runs | length) == 1)' "$out" >/dev/null \
17261726
|| { echo "FAIL: not a single-run SARIF 2.1.0 log"; exit 1; }
1727-
jq -e '.runs[0].tool.driver.name == "Own.NET"' "$out" >/dev/null \
1728-
|| { echo "FAIL: tool.driver.name is not Own.NET"; exit 1; }
1727+
jq -e '.runs[0].tool.driver.name == "Owen"' "$out" >/dev/null \
1728+
|| { echo "FAIL: tool.driver.name is not Owen"; exit 1; }
17291729
# A dangling ruleId or an unlocated result is the #1 reason GitHub rejects
17301730
# a SARIF; startLine is optional (a file-level finding omits it -> // 1).
17311731
jq -e '
@@ -1739,7 +1739,7 @@ jobs:
17391739
and ((.locations[0].physicalLocation.region.startLine // 1) | type == "number"))
17401740
' "$out" >/dev/null \
17411741
|| { echo "FAIL: a result is unlocated or references an undeclared rule"; exit 1; }
1742-
echo "OK: SARIF 2.1.0 — Own.NET driver, every result rule-backed + located"
1742+
echo "OK: SARIF 2.1.0 — Owen driver, every result rule-backed + located"
17431743
- name: The composite action runs end-to-end (non-failing)
17441744
uses: ./
17451745
with:
@@ -1942,15 +1942,18 @@ jobs:
19421942

19431943
# Alpha gate A (issue #202): the single delightful command, proven end-to-end
19441944
# on a clean runner — install -> check -> findings. Packaging only, no
1945-
# analysis-behaviour change: OwnSharp.Cli bundles the *unmodified* extractor
1945+
# analysis-behaviour change: the underlying project stays OwnSharp.Cli
1946+
# internally (P-013; not mass-renamed), but the PUBLISHED identity is the
1947+
# Owen public facade (docs/notes/owen-public-facade.md) — package ID
1948+
# Owen.Cli, command `owen`. Bundles the *unmodified* extractor
19461949
# (ProjectReference; invoked as a child process, same shape own-check.sh
19471950
# already uses) and vendors the *unmodified* ownlang/ core, run by the
19481951
# machine's own Python. Both ubuntu AND windows matter here specifically
19491952
# (not just "more coverage") — a dotnet-tool shim is a native apphost on
19501953
# Windows and a shell script on Unix, so they exercise genuinely different
19511954
# process-launch mechanics; ubuntu-only would not prove the Windows path.
19521955
ownsharp-cli-smoke:
1953-
name: ownsharp CLI (gate A) — clean install -> check -> findings
1956+
name: owen CLI (gate A) — clean install -> check -> findings
19541957
strategy:
19551958
fail-fast: false
19561959
matrix:
@@ -1959,7 +1962,7 @@ jobs:
19591962
defaults:
19601963
run:
19611964
# bash (git-bash on Windows runners) so one script works on both legs;
1962-
# the thing under test is the ownsharp/dotnet/python binaries, not the
1965+
# the thing under test is the owen/dotnet/python binaries, not the
19631966
# shell driving them.
19641967
shell: bash
19651968
steps:
@@ -1976,14 +1979,31 @@ jobs:
19761979
# published there yet, see P-013's Non-goals) -- pack from the source
19771980
# this job already checked out. Deliberately OUTSIDE the timed window
19781981
# below: it is not part of the "install -> check" claim being proven.
1979-
- name: Pack OwnSharp.Cli (pulls in the extractor via ProjectReference)
1980-
run: dotnet pack frontend/roslyn/OwnSharp.Cli/OwnSharp.Cli.csproj -c Release -o "$RUNNER_TEMP/ownsharp-nupkg"
1981-
- name: A minimal leak, in a scratch dir OUTSIDE the repo
1982+
- name: Pack Owen.Cli (pulls in the extractor via ProjectReference)
1983+
run: dotnet pack frontend/roslyn/OwnSharp.Cli/OwnSharp.Cli.csproj -c Release -o "$RUNNER_TEMP/owen-nupkg"
1984+
# --add-source alone is not enough (Codex review, PR #244): `dotnet tool
1985+
# install` queries every configured source (nuget.org included) IN
1986+
# PARALLEL and takes whichever answers first — once a same-numbered
1987+
# version is ever actually on nuget.org, this install could silently
1988+
# resolve from there instead of the just-packed artifact. An isolated
1989+
# nuget.config with <clear/> removes the ambiguity.
1990+
- name: Isolated NuGet.config — the packed artifact is the ONLY visible source
1991+
run: |
1992+
cat > "$RUNNER_TEMP/isolated-nuget.config" <<EOF
1993+
<?xml version="1.0" encoding="utf-8"?>
1994+
<configuration>
1995+
<packageSources>
1996+
<clear />
1997+
<add key="local-artifact" value="$RUNNER_TEMP/owen-nupkg" />
1998+
</packageSources>
1999+
</configuration>
2000+
EOF
2001+
- name: Seed a leak sample, a clean sample, and an unsupported-input sample -- all OUTSIDE the repo
19822002
# Proves the tool needs nothing but itself + Python -- not the Own.NET
19832003
# checkout (a real end user obviously won't have this repo on disk).
19842004
run: |
1985-
mkdir -p "$RUNNER_TEMP/ownsharp-sample"
1986-
cat > "$RUNNER_TEMP/ownsharp-sample/Leak.cs" <<'EOF'
2005+
mkdir -p "$RUNNER_TEMP/owen-sample" "$RUNNER_TEMP/owen-clean" "$RUNNER_TEMP/owen-unsupported"
2006+
cat > "$RUNNER_TEMP/owen-sample/Leak.cs" <<'EOF'
19872007
using System.IO;
19882008
public class Leaky
19892009
{
@@ -1994,19 +2014,90 @@ jobs:
19942014
}
19952015
}
19962016
EOF
2017+
cat > "$RUNNER_TEMP/owen-clean/Clean.cs" <<'EOF'
2018+
using System.IO;
2019+
public class Tidy
2020+
{
2021+
public void Run()
2022+
{
2023+
using var s = new MemoryStream();
2024+
s.WriteByte(1);
2025+
}
2026+
}
2027+
EOF
2028+
cat > "$RUNNER_TEMP/owen-unsupported/app.ts" <<'EOF'
2029+
console.log("not C# -- the .NET/C# frontend is the only one wired in today");
2030+
EOF
19972031
- name: Start the clean-machine timer (install -> check -> findings)
19982032
run: echo "SMOKE_START=$(date +%s)" >> "$GITHUB_ENV"
1999-
- name: dotnet tool install --global (the one install the user runs)
2000-
run: dotnet tool install --global OwnSharp.Cli --version 0.1.0 --add-source "$RUNNER_TEMP/ownsharp-nupkg"
2001-
- name: ownsharp check finds the leak
2033+
- name: dotnet tool install --global (the one install the user runs) -- pins the package ID
2034+
run: dotnet tool install --global Owen.Cli --version 0.1.0 --configfile "$RUNNER_TEMP/isolated-nuget.config"
2035+
- name: owen --help -- language-neutral product framing, explicit included-frontend list, no TypeScript claim
2036+
run: |
2037+
out=$(owen --help)
2038+
echo "$out"
2039+
echo "$out" | grep -qi "^owen " || { echo "FAIL: --help should open with the owen product line"; exit 1; }
2040+
echo "$out" | grep -q "Included frontend" || { echo "FAIL: --help should list the included frontend explicitly"; exit 1; }
2041+
echo "$out" | grep -q '\.cs, \.csproj, \.sln' || { echo "FAIL: --help should name the .NET/C# frontend's extensions"; exit 1; }
2042+
if echo "$out" | grep -qi "typescript"; then
2043+
echo "FAIL: --help must not claim TypeScript support before it's actually wired in"; exit 1
2044+
fi
2045+
- name: owen --version -- pins the command name via PATH resolution alone
2046+
run: |
2047+
out=$(owen --version)
2048+
[ -n "$out" ] || { echo "FAIL: --version printed nothing"; exit 1; }
2049+
echo "OK: owen --version -> $out"
2050+
- name: "owen <unknown command> -- prefix is owen:, not the pre-rebrand ownsharp:"
2051+
run: |
2052+
set +e
2053+
out=$(owen bogus-command 2>&1)
2054+
rc=$?
2055+
set -e
2056+
echo "$out"
2057+
[ "$rc" -ge 2 ] || { echo "FAIL: expected a non-zero exit for an unknown command, got $rc"; exit 1; }
2058+
echo "$out" | grep -q "^owen: unknown command" || { echo "FAIL: expected an 'owen: unknown command' prefix"; exit 1; }
2059+
- name: owen check finds the leak (installed execution, outside any checkout)
20022060
run: |
20032061
set +e
2004-
out=$(ownsharp check "$RUNNER_TEMP/ownsharp-sample" --fail-on-finding 2>&1)
2062+
out=$(owen check "$RUNNER_TEMP/owen-sample" --fail-on-finding 2>&1)
20052063
rc=$?
20062064
set -e
20072065
echo "$out"
20082066
[ "$rc" -eq 1 ] || { echo "FAIL: expected exit 1 (findings), got $rc"; exit 1; }
20092067
echo "$out" | grep -q "OWN001" || { echo "FAIL: expected OWN001 in the output"; exit 1; }
2068+
- name: owen check on clean code exits 0 (negative control)
2069+
run: |
2070+
set +e
2071+
out=$(owen check "$RUNNER_TEMP/owen-clean" --fail-on-finding 2>&1)
2072+
rc=$?
2073+
set -e
2074+
echo "$out"
2075+
[ "$rc" -eq 0 ] || { echo "FAIL: expected exit 0 on clean code, got $rc"; exit 1; }
2076+
- name: owen check --format sarif -- Owen-branded SARIF driver name
2077+
run: |
2078+
out=$(owen check "$RUNNER_TEMP/owen-sample" --format sarif)
2079+
echo "$out" | python3 -c "
2080+
import json, sys
2081+
d = json.load(sys.stdin)
2082+
n = d['runs'][0]['tool']['driver']['name']
2083+
assert n == 'Owen', f'SARIF driver name is {n!r}, expected Owen'
2084+
print('OK: SARIF driver name is Owen')
2085+
"
2086+
- name: owen check on unsupported input fails explicitly -- never a silent clean scan
2087+
run: |
2088+
set +e
2089+
out=$(owen check "$RUNNER_TEMP/owen-unsupported" 2>&1)
2090+
rc=$?
2091+
set -e
2092+
echo "$out"
2093+
[ "$rc" -eq 4 ] || { echo "FAIL: expected exit 4 (no supported input), got $rc"; exit 1; }
2094+
echo "$out" | grep -qi "no supported input" || { echo "FAIL: expected an explicit unsupported-input message"; exit 1; }
2095+
# The exact phrase a genuinely clean C# scan would print (own-check's
2096+
# core render, e.g. "0 findings.") must NOT appear here -- that would
2097+
# mean unsupported input silently looked like a successful clean scan.
2098+
if echo "$out" | grep -q "^0 findings\.$"; then
2099+
echo "FAIL: must not report a clean 0-findings scan for unsupported input"; exit 1
2100+
fi
20102101
- name: Stop the timer -- report it, and gate on a generous regression ceiling
20112102
# A hard ceiling, not a precision claim: CI timing varies with runner
20122103
# load, so this is a regression guard (catch "it now takes 20 minutes"),
@@ -2015,14 +2106,26 @@ jobs:
20152106
elapsed=$(( $(date +%s) - SMOKE_START ))
20162107
echo "install -> check -> findings: ${elapsed}s"
20172108
[ "$elapsed" -lt 240 ] || { echo "FAIL: took ${elapsed}s (ceiling 240s) — see alpha-readiness.md gate A"; exit 1; }
2018-
- name: No Python found -> a fast, actionable failure (never an auto-download)
2109+
- name: No Python found via OWEN_PYTHON -> a fast, actionable failure (never an auto-download)
20192110
run: |
20202111
set +e
2021-
out=$(OWN_PYTHON=/definitely/does/not/exist/python3 ownsharp check "$RUNNER_TEMP/ownsharp-sample" 2>&1)
2112+
out=$(OWEN_PYTHON=/definitely/does/not/exist/python3 owen check "$RUNNER_TEMP/owen-sample" 2>&1)
20222113
rc=$?
20232114
set -e
20242115
echo "$out"
20252116
[ "$rc" -eq 3 ] || { echo "FAIL: expected exit 3 (Python not found), got $rc"; exit 1; }
2026-
echo "$out" | grep -qi "OWN_PYTHON" || { echo "FAIL: expected the OWN_PYTHON-specific message"; exit 1; }
2117+
echo "$out" | grep -q "OWEN_PYTHON" || { echo "FAIL: expected the OWEN_PYTHON-specific message"; exit 1; }
20272118
echo "$out" | grep -Eiq "winget|apt|brew|python.org" || { echo "FAIL: expected an actionable install hint"; exit 1; }
2119+
- name: Legacy OWN_PYTHON still works as a temporary fallback, with a deprecation note
2120+
run: |
2121+
own_python="$(command -v python3 || command -v python)"
2122+
[ -n "$own_python" ] || { echo "FAIL: could not find a python3/python on PATH to test the fallback with"; exit 1; }
2123+
set +e
2124+
out=$(OWN_PYTHON="$own_python" owen check "$RUNNER_TEMP/owen-sample" --fail-on-finding 2>&1)
2125+
rc=$?
2126+
set -e
2127+
echo "$out"
2128+
[ "$rc" -eq 1 ] || { echo "FAIL: expected exit 1 (findings, via the legacy var), got $rc"; exit 1; }
2129+
echo "$out" | grep -qi "OWN_PYTHON is deprecated" || { echo "FAIL: expected a deprecation note when OWN_PYTHON is used"; exit 1; }
2130+
echo "$out" | grep -q "OWN001" || { echo "FAIL: expected OWN001 in the output even via the legacy var"; exit 1; }
20282131

‎README.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ scripts/own-check.sh --format human -- /path/to/your/csharp/repo
2727
```
2828

2929
Needs Python 3.11+ and the .NET SDK on `PATH` — nothing to build, nothing to
30-
`pip install`. A packaged single-command CLI (`ownsharp check`) also exists —
31-
build-and-install-locally today, not yet published to nuget.org; see
30+
`pip install`. A packaged single-command CLI (`owen check`, package
31+
`Owen.Cli`) also exists — build-and-install-locally today, not yet published
32+
to nuget.org; see
3233
[`frontend/roslyn/OwnSharp.Cli/README.md`](frontend/roslyn/OwnSharp.Cli/README.md)
3334
and [`docs/notes/alpha-readiness.md`](docs/notes/alpha-readiness.md) gate **A**.
3435

@@ -921,7 +922,7 @@ ownlang/
921922
test_ownir.py # the OwnIR bridge: C# facts -> core -> OWN001 at the C# site
922923
frontend/roslyn/ # the C# extractor (Roslyn, CI-only) + .cs samples (P-001)
923924
OwnSharp.Extractor/ # ownsharp-extract (dotnet tool): facts only
924-
OwnSharp.Cli/ # ownsharp (dotnet tool, gate A): extractor + vendored core, one install
925+
OwnSharp.Cli/ # owen / Owen.Cli (dotnet tool, gate A): extractor + vendored core, one install
925926
rust/ # the Rust core migration (P-022): own-ir + own-syntax so far,
926927
# oracle-gated against this Python core — see rust/README.md
927928
pyproject.toml # gate: ruff + mypy --strict (see below)

‎README.ru.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ scripts/own-check.sh --format human -- /путь/к/вашему/csharp/репо
2828
```
2929

3030
Нужны Python 3.11+ и .NET SDK в `PATH` — ничего собирать, ничего ставить через
31-
`pip install`. Есть и упакованный однокомандный CLI (`ownsharp check`) — сегодня
32-
собирается и ставится локально, в nuget.org ещё не опубликован; см.
31+
`pip install`. Есть и упакованный однокомандный CLI (`owen check`, пакет
32+
`Owen.Cli`) — сегодня собирается и ставится локально, в nuget.org ещё не
33+
опубликован; см.
3334
[`frontend/roslyn/OwnSharp.Cli/README.md`](frontend/roslyn/OwnSharp.Cli/README.md)
3435
и [`docs/notes/alpha-readiness.md`](docs/notes/alpha-readiness.md), gate **A**.
3536

‎action.yml‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: "Own.NET resource-leak check"
1+
name: "Owen lifetime/resource check"
22
description: >-
33
Scan C# for lifetime/resource leaks the compiler cannot express — event/timer
44
subscription leaks, undisposed IDisposable fields/locals, ignored Subscribe()
@@ -29,7 +29,7 @@ inputs:
2929
required: false
3030
default: "true"
3131
python-version:
32-
description: "Python version for the Own.NET core."
32+
description: "Python version for the Owen core."
3333
required: false
3434
default: "3.13"
3535
dotnet-version:
@@ -39,7 +39,7 @@ inputs:
3939
sarif-file:
4040
description: >-
4141
Where to write the SARIF log when format: sarif (default:
42-
$RUNNER_TEMP/own-net.sarif). The chosen path is echoed back as the
42+
$RUNNER_TEMP/owen.sarif). The chosen path is echoed back as the
4343
sarif-file output regardless.
4444
required: false
4545
default: ""
@@ -54,7 +54,7 @@ outputs:
5454
runs:
5555
using: "composite"
5656
steps:
57-
- name: Set up Python (Own.NET core)
57+
- name: Set up Python (Owen core)
5858
uses: actions/setup-python@v5
5959
with:
6060
python-version: ${{ inputs.python-version }}
@@ -64,7 +64,7 @@ runs:
6464
with:
6565
dotnet-version: ${{ inputs.dotnet-version }}
6666

67-
- name: Own.NET leak check
67+
- name: Owen leak check
6868
id: own
6969
shell: bash
7070
env:
@@ -88,16 +88,16 @@ runs:
8888
# passed so the true exit code (0 clean / 1 findings / >=2 hard error) is
8989
# captured *after* the file is written; whether a finding fails the step
9090
# is the action's own fail-on-finding (default: let code scanning gate).
91-
sarif="${OWN_SARIF_FILE:-$RUNNER_TEMP/own-net.sarif}"
91+
sarif="${OWN_SARIF_FILE:-$RUNNER_TEMP/owen.sarif}"
9292
set +e
9393
"$check" --root "${{ github.action_path }}" --format sarif \
9494
--severity "$OWN_SEVERITY" --fail-on-finding -- "$OWN_PATH" > "$sarif"
9595
rc=$?
9696
set -e
9797
echo "sarif-file=$sarif" >> "$GITHUB_OUTPUT"
98-
echo "Own.NET wrote SARIF to $sarif ($(wc -c < "$sarif" 2>/dev/null || echo 0) bytes; own-check rc=$rc)"
98+
echo "Owen wrote SARIF to $sarif ($(wc -c < "$sarif" 2>/dev/null || echo 0) bytes; own-check rc=$rc)"
9999
if [ "$rc" -ge 2 ]; then
100-
echo "::error::Own.NET hard error (bad facts / drifted contract)"
100+
echo "::error::Owen hard error (bad facts / drifted contract)"
101101
cat "$sarif" >&2 || true
102102
exit "$rc"
103103
fi
@@ -107,7 +107,7 @@ runs:
107107
# fails the redirection (exit 1, outside own-check's 0/1/>=2 contract).
108108
# Surface it; never let it pass as a gated-off finding with no SARIF.
109109
if [ ! -s "$sarif" ]; then
110-
echo "::error::Own.NET produced no SARIF at $sarif (own-check rc=$rc) — the run or its redirection failed"
110+
echo "::error::Owen produced no SARIF at $sarif (own-check rc=$rc) — the run or its redirection failed"
111111
exit 1
112112
fi
113113
if [ "$OWN_FAIL_ON_FINDING" = "true" ] && [ "$rc" -eq 1 ]; then

0 commit comments

Comments
 (0)