Skip to content

fix(explain): resolve console-script name, greening the rubric gate#1

Open
OriNachum wants to merge 4 commits into
mainfrom
fix/explain-self-rubric-gate
Open

fix(explain): resolve console-script name, greening the rubric gate#1
OriNachum wants to merge 4 commits into
mainfrom
fix/explain-self-rubric-gate

Conversation

@OriNachum

@OriNachum OriNachum commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Why

The CI lint job runs uv run teken cli doctor . --strict, and that gate was
red on main — 25/26 bundles. Because it is red for reasons unrelated to any
given change, every PR opened against this repo inherited a failing lint job,
docs-only ones included. This unblocks the lane.

The failing bundle was explain_self. The rubric derives the CLI's self-name
from the [project.scripts] entry point — fleet — and probes explain fleet.
The catalog only keyed the distribution name, ("fleet-cli",), so the probe
errored.

What changed

  • fleet/explain/catalog.py — register ("fleet",) alongside
    ("fleet-cli",); both key the root entry. Gate goes 25/26 → 26/26.
  • fleet/explain/__init__.py — the explain remediation hint told the
    reader to run fleet-cli explain fleet-cli. fleet-cli is the distribution
    name, not a console script, so following the hint produced a
    "command not found". It now names fleet explain fleet, which is executable.
  • tests/test_cli.pytest_explain_self_name_matches_console_script pins
    that both spellings render identically to the bare root. The pre-existing
    test_every_catalog_path_resolves could not have caught this: it walks
    catalog keys → commands, never the reverse, so a missing key is
    invisible to it.
  • CLAUDE.md — expanded from the scaffold seed into full repo guidance
    (CLI contracts, noun-group specialization checklist, test-module map, the
    fleet vs fleet-cli split, mesh identity, rubric invariants).
  • README.md — the quickstart printed uv run fleet-cli whoami and
    uv run fleet-cli learn, neither of which runs. Both now name the console
    script, and a line states the split outright. Verified by executing
    fleet whoami, fleet learn, and python -m fleet whoami.

Verification

Ran the exact CI matrix locally, all green:

Check Result
black --check / isort --check-only / flake8 / bandit pass
markdownlint-cli2 pass
teken cli doctor . --strict 26/26, 0 errors (was 25/26)
pytest -n auto --cov=fleet 23 passed, coverage above the 60% gate

The remediation hint was executed verbatim to confirm it resolves:
fleet explain fleet → exit 0.

Reviewed by colleague

ask-colleague review (read-only, isolated worktree) found no blocking bugs and
endorsed the dual-key approach over collapsing prog=. Two notes came back:

  1. Applied — the new test leaned on transitivity to relate the
    distribution-name spelling to the root entry; it now compares each spelling
    to the bare-root rendering directly.
  2. Deferred, deliberately — it suggested also rewriting the
    fleet-cli explain fleet-cli examples in the _EXPLAIN catalog body and
    learn.py. Doing only those two would make the surface less consistent,
    not more: all ~30 usage examples across the catalog and learn are spelled
    fleet-cli <verb>, and none of them run. Reconciling that properly means
    changing prog= too, which several tests pin (usage: fleet-cli,
    subject == "fleet-cli", "fleet-cli doctor"). That is a separate,
    deliberate change — documented in CLAUDE.md and tracked in Reconcile the fleet vs fleet-cli naming split #2.

Scope note

This bundles a code fix with the CLAUDE.md rewrite because they are coupled:
the doc's "Known failing check" section existed to describe precisely the bug
this PR fixes, so landing one without the other ships a stale document.

Found along the way

  • Tracked in Reconcile the fleet vs fleet-cli naming split #2 — the full fleet / fleet-cli reconciliation (prog= and
    the ~30 usage examples that don't run).
  • Tracked in docs/skill-sources.md omits the vendored recall + remember skills #3docs/skill-sources.md documents 12 skills, but 14
    are vendored on disk; recall and remember have no provenance rows, even
    though both SKILL.md files point at that ledger for their provenance. Per
    CHANGELOG.md they came from eidetic-cli in 0.4.0. Not reconciled here: the
    upstream version and last-synced date are facts to be recorded by whoever
    vendored them, not guessed by a reader. Noted in CLAUDE.md.
  • Not fixed, deliberatelydevex pr lint reports one
    user-dotfile-reference violation in CHANGELOG.md. It is pre-existing on
    main (the lint scans whole files, not the diff) and sits in prose describing
    a path that is home-dir by design. Rewriting a shipped changelog entry to
    satisfy a lint rule seemed worse than leaving it.

OriNachum and others added 2 commits July 10, 2026 07:01
`teken cli doctor . --strict` failed its `explain_self` bundle (25/26),
turning the CI `lint` job red on every PR regardless of content. The
rubric derives the CLI's self-name from the `[project.scripts]` entry
point (`fleet`) and probes `explain fleet`, but the catalog only keyed
the distribution name `("fleet-cli",)`.

Register both spellings against the root entry, and pin the invariant in
pytest so it surfaces in the fast suite rather than only in the slow gate.

Drive-by: the `explain` remediation hint told the reader to run
`fleet-cli explain fleet-cli` — `fleet-cli` is the distribution name, not
a runnable command, so the hint led to "command not found". It now names
`fleet explain fleet`, which is executable.

Also expands CLAUDE.md from the scaffold seed into full repo guidance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjyqmVt8wog8m6xGtWGYND
Colleague review: the previous form relied on transitivity
(`fleet == fleet-cli`, then `bare-root == fleet`) to establish that the
distribution-name spelling matches the root entry. Compare each spelling
to the bare-root rendering directly instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjyqmVt8wog8m6xGtWGYND
@OriNachum

Copy link
Copy Markdown
Contributor Author

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Jul 10, 2026

Copy link
Copy Markdown

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Fix explain self-name resolution for console script (fleet) and unblock CI lint gate

🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Register both fleet (console script) and fleet-cli (distribution) as explain root keys.
• Fix the explain unknown-path remediation hint to use a runnable command.
• Add a regression test so the rubric’s explain_self invariant fails fast in pytest.
Diagram

graph TD
  A["CI lint: teken doctor"] --> B["pyproject scripts: fleet"] --> C["CLI entry: fleet.cli:main"] --> D["explain.resolve()"] --> E[("explain catalog ENTRIES")]
  F["pytest: test_explain_self_name"] --> C --> D --> E
  E --> G["Root entry markdown"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Unify naming by changing `prog=`/docs to `fleet` everywhere
  • ➕ Eliminates split-brain between runnable command and display/distribution name
  • ➕ Removes need for dual-key aliases in the catalog
  • ➖ Broad, user-visible surface change (help output, docs, tests)
  • ➖ Higher risk and larger PR; many pinned expectations would need updating together
2. Derive/alias console-script names automatically at runtime
  • ➕ Avoids manual catalog maintenance when console-script names change
  • ➕ Could generalize to other future aliases
  • ➖ Adds complexity and coupling to packaging metadata
  • ➖ May be at odds with the current “simple, no runtime deps” CLI philosophy
3. Fix the rubric gate to probe the distribution name instead of console script
  • ➕ Moves responsibility out of this repo; avoids dual-key catalog entries
  • ➖ Not under this repo’s control; slower to land
  • ➖ Still leaves the CLI remediation hint and real-world runnable-command confusion

Recommendation: The PR’s dual-key approach is the best minimal-risk fix: it satisfies the rubric’s console-script probe (fleet) without forcing a broad rename away from the existing fleet-cli doc/prog conventions. A full naming collapse (including prog= and all examples) should be a deliberate, separate change because it affects many pinned outputs and user-facing strings.

Files changed (6) +294 / -23

Bug fix (2) +11 / -3
__init__.pyFix 'explain' remediation hint to use runnable console script +4/-1

Fix 'explain' remediation hint to use runnable console script

• Updates the unknown-path remediation message to recommend 'fleet explain fleet' instead of the non-runnable 'fleet-cli' console command, with clarifying comments.

fleet/explain/init.py

catalog.pyAlias 'fleet' and 'fleet-cli' to the root explain entry +7/-2

Alias 'fleet' and 'fleet-cli' to the root explain entry

• Documents the rubric’s console-script-derived self-name behavior and registers '("fleet",)' alongside '("fleet-cli",)' so both resolve to the root catalog entry.

fleet/explain/catalog.py

Tests (1) +18 / -0
test_cli.pyAdd regression test for 'explain' self-name resolution +18/-0

Add regression test for 'explain' self-name resolution

• Adds 'test_explain_self_name_matches_console_script' to assert 'explain', 'explain fleet', and 'explain fleet-cli' render identically, ensuring the rubric issue is caught in pytest.

tests/test_cli.py

Documentation (2) +264 / -19
CHANGELOG.mdAdd 0.4.1 release notes for 'explain' self-name fix +16/-0

Add 0.4.1 release notes for 'explain' self-name fix

• Adds a new 0.4.1 entry documenting the rubric-gate fix, the remediation-hint correction, the regression test, and the CLAUDE.md expansion.

CHANGELOG.md

CLAUDE.mdReplace scaffold seed with full repo/CLI guidance +248/-19

Replace scaffold seed with full repo/CLI guidance

• Rewrites CLAUDE.md into a detailed working guide covering CLI contracts, 'fleet' vs 'fleet-cli' naming, test module responsibilities, rubric invariants, and development workflows.

CLAUDE.md

Other (1) +1 / -1
pyproject.tomlBump project version to 0.4.1 +1/-1

Bump project version to 0.4.1

• Updates the package version from 0.4.0 to 0.4.1 to match the changelog entry and release notes.

pyproject.toml

@qodo-code-review

qodo-code-review Bot commented Jul 10, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 2 rules

Grey Divider


Remediation recommended

1. Reserved explain key collision ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
Registering ("fleet",) as an alias for the root explain entry permanently occupies that
command-path tuple because fleet.explain.resolve() only does exact-key lookups. This makes it
impossible to later introduce a real fleet noun group with its own explain fleet documentation
without redesigning the catalog scheme, and it also conflicts with the new guidance that suggests
adding a fleet noun group.
Code

fleet/explain/catalog.py[R125-126]

+    ("fleet-cli",): _ROOT,  # distribution name (prog=, doc text)
+    ("fleet",): _ROOT,  # console-script name (the runnable command)
Evidence
The explain resolver only checks if path in ENTRIES, so any alias key added to ENTRIES is a
permanent reservation of that tuple; adding ("fleet",) therefore blocks using that tuple for a
future noun. The newly-expanded CLAUDE.md simultaneously suggests adding a fleet noun group and
adding explain entries keyed by (<noun>,), which would collide with the reserved ("fleet",)
alias.

fleet/explain/init.py[12-23]
fleet/explain/catalog.py[123-127]
CLAUDE.md[137-160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`fleet/explain/catalog.py` now reserves the explain path `("fleet",)` for the root entry to satisfy the rubric’s `explain fleet` probe. Because `fleet.explain.resolve()` only checks for exact key membership, this reservation prevents the project from ever using `fleet` as a real noun group in the explain namespace (i.e., having docs for `fleet <noun>` where `<noun>` is literally `fleet`).

This is especially confusing because `CLAUDE.md` currently suggests adding a future noun group named `fleet`.

### Issue Context
- `resolve()` performs exact tuple lookups, so aliases occupy the key space permanently.
- The repository guidance explicitly mentions `fleet` as a candidate noun group name.

### Fix Focus Areas
- fleet/explain/catalog.py[123-127]
- fleet/explain/__init__.py[12-23]
- CLAUDE.md[137-160]

### Suggested fix approaches (pick one)
1. **Doc-only mitigation (minimal change):** Update `CLAUDE.md` to explicitly state that `fleet` is reserved as the CLI self-name in `explain`, and recommend choosing noun groups that don’t collide with the console-script name.
2. **Catalog scheme mitigation (more flexible):** Rework `fleet.explain.resolve()` to support a dedicated “self/root alias” mechanism that doesn’t permanently consume a plausible noun key (requires an explicit escape/namespace mechanism if you ever want both root-alias and a `fleet` noun).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread fleet/explain/catalog.py Outdated
`uv run fleet-cli whoami` and `uv run fleet-cli learn` both failed with
"command not found": `fleet-cli` is the distribution name, while `fleet`
is the sole `[project.scripts]` entry and therefore the only runnable
command. Name the console script, and say so explicitly so the split
does not surprise the next reader.

Verified by executing all three forms (`fleet whoami`, `fleet learn`,
`python -m fleet whoami`).

The broader reconciliation of `prog=` and the ~30 `fleet-cli <verb>`
usage examples is tracked in #2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjyqmVt8wog8m6xGtWGYND
Qodo review: registering ("fleet",) as a root alias permanently occupies
that command-path tuple, because resolve() does exact-tuple lookup. The
guidance added in this PR then offered `fleet` as an example noun-group
name — following it would overwrite the root alias and re-break the
rubric's explain_self bundle.

Take the doc-only mitigation. The collision is inherent to the rubric,
not to the catalog's lookup scheme: explain_self probes `explain <console
script>` and requires the root entry back, so no aliasing mechanism can
serve both root docs and a `fleet` noun's docs from that path.

Reserve the name in the catalog comment and in CLAUDE.md, and steer the
drone-fleet noun to `drone`/`swarm`/`mission`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CjyqmVt8wog8m6xGtWGYND
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant