docs/development.md
Development
Read the relevant sections before changing QML, JavaScript, configuration, tests, packaging, or runtime behavior. AGENTS.md defines project boundaries and required checks. The usage guide describes supported behavior; GitHub Issues owns work status. All code paths below are relative to the repository root.
Work from a checkout
Install make, Python 3, GNU gettext, and the Plasma runtime requirements in
the README. The check suite also needs Qt 6 QML
lint/test tools, ShellCheck, xmllint, and jq. The pinned
CI workflow lists the complete test environment;
package names vary by distribution.
| 1 | git clone https://github.com/Lucenx9/codexbar-plasma.git |
| 2 | cd codexbar-plasma |
| 3 | make check |
| 4 | make install |
make install builds and installs the curated .plasmoid archive. Tests and
agent instructions are not installed. Reload Plasma after installing changed
QML, using the runtime instructions.
For an existing checkout, ./install.sh builds, installs, and restarts Plasma.
From this checkout, make update installs the latest immutable GitHub release
using the bundled helper. It does not pull Git changes or install local edits.
For those, use make install or ./install.sh. Release-package users can use
the widget's update settings without a checkout.
Ownership and implementation
contents/ui/main.qmlowns applet processes, refresh/account coordination, selected state, configuration updates, and external effects. Its adapters supply the panel and popup.contents/ui/components/CompactRepresentation.qmlrenders the panel;FullRepresentation.qmlin the same directory renders the popup. Components are presentation-only and receive normalized data plus an explicit parent API such asappletorconfigPage.contents/ui/configProviders.qmlowns provider setup processes, prompts, configuration writes, and effects. Its pure protocol and command-planning modules live incontents/ui/config/.- Pure applet modules live in
contents/ui/*.js; their public interfaces have direct tests intests/tst_*.qml. Configuration is declared incontents/config/main.xmland bound throughcfg_*in config pages andPlasmoid.configurationat runtime.
Before changing behavior, identify its owning QML page, config entry, CLI input, external effects, and cheapest behavioral test. Read the existing implementation and tests. Preserve compatibility and unexpected behavior until evidence or the request shows it should change.
When multiple pages consume one CLI envelope, share the bounded record/envelope contract and keep page-specific projections separate. Avoid both duplicate raw parsing and one lossy result that erases different UI semantics. For a new CLI field, update normalization, rendering, relevant checks, and affected guides. Reconcile the linked issue against its acceptance criteria.
Use names that identify provider, source, account, window, or unit when ambiguous.
build*, format*, provider*Url, *Rows, and *Text helpers stay pure;
refresh*, load*, parse*, select*, set*, and process* may mutate state.
Put non-obvious lifecycle state in names such as connected*, pending*,
*Memo, *Revision, and *Initialized, and update it beside its effect.
Comments explain a workaround or contract, rather than restating assignments.
For repeated JavaScript in delegates, timers, or callbacks, use named helpers. For repeated or bulky UI blocks, use small presentation-only components. Extraction must hide complexity, not merely reduce line count.
Behavioral contracts to preserve
- Quota thresholds come from
QuotaThresholds.js, shared by notifications and usage-bar markers.limitResetArmThresholddetects limit resets separately; it is not a warning threshold.main.qmlresets opaque notification state throughNotificationPlanner.js; its internalNotificationMemo.jspreserves provider status baselines. Settings changes must not announce an ongoing incident again or swallow an incident arriving during refresh. Keep these decisions pure and covered bytests/tst_notification_planner.qmlandtests/tst_notification_memo.qml. Predictive warnings prime silently and fire on a new projected-exhaustion transition. - Sessions normalize display fields only. Discard
cwd,transcriptPath, IDs, and PIDs; never render, open, or follow them. Remote/SSH host focus is a macOS-only non-goal. PanelRules.jsevaluates the displayed quota without effects. Direct missing quotas stay omitted;runOutdepends onpaceWarningActive. Panel visibility, order, and metric settings preserve the minute clock and icon fallback and must not fetch data or change notification state.- Privacy projects display records without changing cached snapshots or account
command keys. Preserve config keys and pending defaults across all settings
pages.
PopupRefreshPolicy.jshandles freshness and failed-attempt cooldown, including in-flight/queued work; opening the popup must not start cost scans. See the settings decision record. - Validate
credits.codexCreditLimitas finite non-negative amounts, clamped percentages, and bounded reset metadata. Its denominator belongs only to that nested record, never to a plainusage.creditsbalance.
QML conventions
- Use
PlasmaComponentsfor panel/popup controls and Kirigami/Qt Quick Controls in settings. UseContainmentItemonly for containment/panel/desktop code, not this applet's root. - Keep
metadata.json,contents/ui,contents/config/main.xml, andcontents/config/config.qmlin the standard package layout. - Use checkboxes for booleans, spinboxes/sliders for numbers, text fields for short strings, and a combobox for more than three choices.
- Give visual children a size through layouts, anchors, implicit sizes, or explicit compact dimensions. Items otherwise default to 0x0.
- Use layout minimum/preferred sizes, implicit dimensions, and
Kirigami.Unitsinstead of panel-size magic numbers. Use anchors/layouts rather than bindings to sibling geometry. - Prefer declarative bindings. Move repeated or expensive calculations into helpers or cached properties. Avoid heavy JavaScript in delegates, compact rendering, timers, and DataSource callbacks; profile before optimizing.
- Keep delegates stable. Add clipping, shaders, or nested layouts only for a visible need.
- External Repeater/view delegate components must declare
required property var modelDatainside the component. An alias assignment from the parent does not establish the delegate's scope;qmllintmay miss the resulting runtime error. - Use
qmlformatfor new files or a dedicated formatting change, then inspect the diff. Existing files have no formatter baseline, so preserve formatting during unrelated work. - Keep
qmllsin the editor. Machine-specific.qmlls.inifiles stay out of Git; language-server diagnostics do not replacemake check.
For translation/catalog changes, follow Translations.
When changing provider identity, check keys, CLI aliases, title, color,
docs/dashboard/login URLs, icon assets, and scripts/test_provider_icons.sh.
Repeat this comparison during official CLI release audits. The pinned 0.56.2
audit found the same 69 IDs as 0.55.0; the OpenRouter fallback already uses
https://openrouter.ai/activity. Treat the catalog as fallback presentation data
and use the dated evidence, not an
assumed current upstream count.
Regression checks
Use direct adversarial QtTests for pure public behavior. Assert QML wiring, effect ownership, and lifecycle ordering statically. Use runtime checks where static checks cannot establish the behavior. Avoid assertions on private helper names or body decomposition when public behavior is already covered.
scripts/lib/qml_surfaces.py defines QML/JS file groups for the Makefile,
hardening checks, and translation extraction. Existing globs cover new files;
add a glob only for a new directory. scripts/test_qml_hardening.sh rejects
uncovered QML/JS files.
- Use
require_in_surface/reject_in_surfacein shell, orSurface.require,Surface.function_body, andSurface.id_blockin Python, for rules applying to the whole applet or config page. Moving code between files then preserves the check. Use*_in_fileonly for actual per-file contracts, such as safe icon fallback in each delegate. - Use
require_definition_where_usedorSurface.require_definition_where_usedfor shared unqualified helpers. A declaration in another QML/JS file does not satisfy the caller's scope. These helpers also check the page root when components callapplet.foo(...). A plain search forfunction foo(anywhere in a group can miss broken callers. - Local bindings for
hasOwnKey,isUnsafeObjectKey,copyObject, andshellQuotedelegate tocontents/ui/Guards.js. Share the implementation through that.pragma librarymodule, including in config pages.tests/tst_guards.qmlcovers behavior;scripts/test_security_regressions.shrejects implementations duplicated elsewhere.
Run the relevant check during iteration, then the required make check gate.
Read its output for tool failures and skips. CI uses the pinned Plasma image in
the workflow, with explicit import warnings and
QtTests configured to reject skips. A local machine missing QML modules may
provide less coverage; report what actually ran.
make check disables unqualified-name warnings because Plasma injects helpers
such as i18n() as context properties. It validates AppStream metadata when
kpackagetool6 is available and reports a skip otherwise. On older local Plasma
packages without QML type metadata, import/type coverage may be partial; CI
supplies the metadata. To require CI's strict checks locally:
| 1 | QML_TEST_REQUIRE_NO_SKIPS=1 make check QMLLINT_FLAGS='--import warning --unqualified disable' |
CI also runs smoke scenarios under Xvfb and retains screenshots and logs as workflow artifacts. Release publication requires both check and smoke jobs.
Popup smoke tests
Run the popup smoke test from a graphical Plasma 6 session:
| 1 | make smoke |
This requires Python 3, GNU gettext, plasmawindowed, dbus-run-session, and
the Plasma, Kirigami, and KDE desktop control QML modules. README captures also
require the Breeze Dark color scheme. Localized scenarios use the UTF-8 locales
in the translation guide; CI generates them during setup.
The runner opens a temporary applet for each scenario, captures it, and closes
it automatically. List the available scenarios with:
| 1 | python3 scripts/smoke_popup.py --help |
Select one scenario or choose a new artifact directory:
| 1 | make smoke SMOKE_ARGS='--scenario long-text' |
| 2 | python3 scripts/smoke_popup.py --scenario normal --output /tmp/codexbar-preview |
| 3 | python3 scripts/smoke_popup.py --scenario panel-minimal --renderer opengl |
| 4 | python3 scripts/smoke_popup.py --scenario readme-overview --renderer opengl --output /tmp/codexbar-readme |
Use --renderer opengl on a graphical session with OpenGL for accurate provider
icon colors. The default software renderer checks layout and behavior, but does
not render Kirigami's icon color masking. Qt documents the software renderer's
effect limitations.
The command prints the artifact directory, defaulting to dist/smoke/run-*.
Each scenario produces a PNG and a process log; results.json records pass or
failure. Existing output directories are never overwritten. The timeout is
30 seconds per scenario and can be changed with --timeout 60.
The runner copies the current applet into temporary XDG directories under a separate applet ID and uses a private D-Bus session. It sets a synthetic CLI path and disables updates and notifications before QML starts. It does not install a package, use real provider accounts, change the installed widget's settings, or restart Plasma. Temporary files and preview processes are removed on completion, timeout, or interruption.
The capture component selects the real popup views and waits for the expected
state before using Qt's
grabToImage.
QML errors, missing captures, early exits, and timeouts fail the command.
Screenshots still need visual review: this is not a pixel-comparison test and
does not exercise panel placement, key-event dispatch, or the real CLI.
Synthetic payloads cover a subset of the CLI 0.56.2 contract; fixture dates
are relative to run time. Typography uses Noto Sans and Breeze icons.
make check covers the runner's portable isolation and failure-handling tests
and lints the capture QML.
make smoke additionally requires the graphical environment and reports a
failure rather than silently skipping when that environment is unavailable.
Runtime verification
After extracting components/delegates, install or upgrade the widget and inspect
recent logs for ReferenceError, TypeError, and SyntaxError:
| 1 | ./install.sh |
| 2 | journalctl --user -u plasma-plasmashell.service --since '2 minutes ago' --no-pager \ |
| 3 | | rg -n 'app\.codexbar|CodexBar|ReferenceError|TypeError|SyntaxError|file://.*/app.codexbar' |
Ignore unrelated widget logs unless they mention app.codexbar.
For a panel smoke check when plasma-sdk is installed:
| 1 | plasmoidviewer -a "$PWD" -l topedge -f horizontal |
Use plasmawindowed app.codexbar.plasma for an installed-widget check.
Restarting/replacing plasmashell is a final runtime check, not the edit loop.
Use qmlprofiler only after reproducing a performance problem, and keep traces
in ignored dist/review/ or the OS temp directory.
CLI probes and packaging
The widget expects a working codexbar. Probe the selected official version
when investigating a contract; keep raw account output out of logs and commits.
| 1 | codexbar usage --format json --json-only |
| 2 | codexbar usage --provider codex --status --format json --json-only |
| 3 | codexbar usage --provider codex --all-accounts --format json --json-only |
| 4 | codexbar cost --format json --json-only |
| 5 | codexbar config providers --format json --json-only |
| 6 | codexbar sessions --json-v2 |
For packaging changes, run make package after make check.
Packaging needs Python 3 and GNU gettext. It compiles po/*.po to
contents/locale/<language>/LC_MESSAGES/plasma_applet_app.codexbar.plasma.mo
and includes the catalogs in the archive. Generated .mo files stay out of Git.
For string extraction and catalog updates, follow Translations.
The PACKAGE_FILES list in the Makefile defines the archive contents, including
README screenshots. Publishing the resulting dist/codexbar-plasma.plasmoid
in a GitHub Release requires user authorization.
If a KDE Store channel is introduced, use its KNewStuff/Discover update path for
that channel. WidgetKit, Sparkle, Keychain/Full Disk Access UI, and macOS app
implementation code remain outside this standalone Plasma repository.
Delivery and CI
Use a codex/ branch and a PR for repository changes. Direct pushes require an
explicit exception for the current task. A request to push directly does not
authorize weakening protection rules or bypassing failed checks.
Every PR title follows Conventional Commits:
type(scope): description, with an optional scope and an
optional ! before the colon for breaking changes. Allowed types are feat,
fix, docs, style, refactor, perf, test, build, ci, chore, and
revert. Use a short description of the final change, for example
fix(accounts): release stale requests or docs: clarify installation.
Keep the title current when scope changes, and retain that format for the squash
commit. The pr-title check validates new and edited titles; title edits run
this small workflow without rerunning the full widget suite.
The default branch rules require a PR and successful GitHub Actions check,
smoke, and pr-title jobs, tested against the current base branch. The rules
also prevent
deletion and force pushes. Copilot review remains enabled; no additional human
approval count is required. The maintainer can merge their own PR once its
checks pass. Inspect the effective rules with:
| 1 | gh api repos/Lucenx9/codexbar-plasma/rules/branches/main |
The agent owns delivery through these completion criteria:
- After each push, identify the exact commit and its GitHub runs. Follow the required checks and other applicable CI runs until they finish. Queued or running checks are pending, not a completed delivery. Results for an older commit do not validate a newer push.
- Inspect failed jobs and fix failures caused by the change. Rerun a job only when evidence supports a transient infrastructure failure; repeated failures need diagnosis. Report an external blocker with the run link and failing job. Treat unexpected skips, cancelled runs, and missing required checks as unresolved. The release job is intentionally skipped on non-tag runs.
- For a PR-only request, finish when the current PR checks pass and the PR body records their results. Report it as ready for review, not merged. Merge only when authorized; check the current head and base again before merging.
- After an authorized merge or direct push, follow the runs for the resulting
maincommit too. Reconcile related issues and report the commit, final CI outcome, and any remaining blocker. Fix a post-merge failure through a PR unless the user explicitly authorizes another delivery route.
Keep progress updates brief while waiting. If the session is interrupted, leave the exact commit, run links, and remaining checks in the handoff so work can resume without treating pending CI as success. A recurring automation is needed only when the user asks for monitoring beyond the active task.
Changelog and releases
CHANGELOG.md is the source of notable widget changes and future GitHub release notes. It follows Keep a Changelog. History starts at 0.2.35, using its published notes; earlier releases remain linked on GitHub.
For every user-visible change, the agent adds or revises an Unreleased entry
in the same commit. Describe the observable result and any upgrade action.
Group entries under Added, Changed, Deprecated, Removed, Fixed, or Security;
include only categories with entries. Group related commits into one useful
entry. Internal refactors, tests, and routine instruction edits need no entry
unless they affect users or contributors' supported workflows; explain that
decision in the PR or direct commit body. Issues continue to own future work.
Before an authorized release:
- Compare the diff since the last release with
Unreleased, including direct commits to main. Correct omissions and remove claims that have not shipped. - Move the entries into
## X.Y.Z - YYYY-MM-DD, leaving an empty## Unreleasedfirst. Use the release date, keep versions newest first, and setKPlugin.Versioninmetadata.jsonto that same version. Add a full-diff link with absolute GitHub URLs so it works in both the package and release. - Run
make check,make package, andpython3 scripts/changelog.py --tag vX.Y.Z. Review the extracted notes before committing and pushing the matching tag. Tag publication needs authorization.
make check validates headings, categories, dates, ordering, and the newest
version against metadata. The tag workflow also requires an empty Unreleased
section and an exact tag match. It extracts the version's body into ignored
dist/release-notes.md and publishes it through the release action's
body_path.
The archive includes the changelog. Keep published sections historical; prepare
new changes under Unreleased and correct factual errors explicitly. Existing
GitHub releases are not rewritten when this workflow is introduced.
These checks validate structure and release consistency. The agent must still compare the final change with the entries to catch missing or misleading notes.
Repository maintenance
The agent owns repository consistency in every task. Include dependent updates in the same change, without waiting for the user to name each file.
Before editing, trace the affected behavior through its callers, configuration, tests and fixtures, scripts, CI workflows, packaging and metadata, translations, documentation and examples, and related issues. Inspect the relevant files; filename searches alone do not establish whether a dependency is affected.
Before delivery, review the final diff and search for old names, paths, commands, defaults, and claims affected by the change. Update each dependent reference or confirm that it remains valid. Regenerate affected tracked outputs through their existing generators. Remove obsolete references when replacing an interface; preserve compatibility requirements and pinned historical evidence.
Fix stale material discovered during the task when the correction is clear, safe, and within scope. If it needs a separate decision, external contract, or substantial unrelated work, record the evidence and remaining work in an existing or new issue under the publication rules in AGENTS.md. Report the exact blocker when issue publication is not authorized. Complete independent work and make any unresolved inconsistency explicit at handoff.
Completion requires consistent affected files, the applicable checks below, and accurate issue status after delivery. Passing tests alone does not establish consistency. In the PR verification section or direct commit body, summarize the dependent updates and any unresolved gaps. Version and dependency changes need compatibility evidence; bump versions only as part of their actual update or release workflow. A date change alone is not maintenance.
Documentation and work tracking
These steps cover documentation and issues within repository maintenance, including direct commits authorized by the user. Do not wait for a separate documentation request.
- Before editing, inspect the relevant current guides, pinned contract evidence, and existing issues. Search before creating a work item. Each issue needs an intended result, acceptance criteria, and any blocker with an exact version or source. A migration or an older audit is not a fresh CLI verification.
- Reconcile the final diff with documentation. Update setup and requirements in README, behavior/defaults in the usage guide, and contributor workflows here. Keep technical contracts in their existing focused guides. Update affected examples and screenshots, remove obsolete claims, and maintain the docs index. For each changed behavior, identify its guide or explain why none is affected.
- Run
make checkand applicable example/package checks. Inspect documented commands against their implementation. Mechanical checks catch broken paths, indexing, and budgets; the agent must also review whether the prose is true. Record documentation impact and verification in the PR. For a direct commit, record them in its body and reconcile issue status after the push. - Use
Closes #123in a PR description or direct commit message only when every acceptance criterion is met. For partial work, useRefs #123and update the issue's remaining work and blockers. Confirm closure after delivery; creating an issue or documenting a gap does not complete it. TODO.md remains a small entry point without copied issue status or checkboxes.
Keep proposal status visible inside the document: proposed, implemented, or superseded, with the exact verified contract and a tracker link where relevant. Examples in a proposal must be identified as proposed commands. Dated audits retain their original findings; add a successor link when a new audit replaces them. Link current behavior separately so historical recommendations are not mistaken for unfinished tasks.
During a new official CLI audit, recheck blocked-upstream issues against that
release. During release preparation, check setup, requirements, defaults, and
changed features against current guides. Record actual checks rather than
updating a date to imply verification. Keep task status in GitHub Issues and
update the canonical explanation rather than copying it into several documents.
This follows Google's documentation practices and docs as code. GitHub's closing-keyword rules apply when the change reaches the default branch. Projects are optional views of issues; this repository does not require a separate manually maintained board.
Maintaining agent instructions
Keep permanent rules in AGENTS.md and work status in GitHub Issues. Add a rule for an observed failure or repeated friction. Put task-specific details in the relevant section of this guide and add a root pointer stating when to read it. Maintain links and the documentation index in the same change.
The repository enforces a 16 KiB AGENTS.md budget, with a working target below 200 lines. These are local maintenance choices. Codex's default budget is 32 KiB across discovered project instructions, so leave room for other files. See Codex discovery.
OpenAI describes using a short instruction file as an index into maintained docs. This motivates our conditional pointers, rather than a duplicate feature inventory. See OpenAI's engineering experience.
Keep CLAUDE.md as the single @AGENTS.md import. Imports load their full
contents; adding imports for every guide would restore the context cost.
Anthropic recommends concise instructions, generally under 200 lines, rather
than imposing that as a loading limit. See Claude memory.
Keep .github/copilot-instructions.md limited to review guidance and consistent
with AGENTS.md. GitHub assigns that file higher precedence than AGENTS.md;
repository prose cannot reverse product precedence. See
GitHub instruction precedence.