alissa-tools-github-devloop
The alissa.tools.github.devloop module: a GitHub watcher that turns
alissa:develop-labeled issues into fresh developer sessions — the
developer-side counterpart of
alissa-tools-github-reviewloop.
This distribution ships only that module. Everything above it —
alissa, alissa.tools, alissa.tools.github — is a
PEP 420 namespace package with no
__init__.py, so other distributions in other repositories can contribute
their own packages under the same namespace:
alissa/ ← namespace (no __init__.py)
└── tools/ ← namespace
└── github/ ← namespace
├── devloop/ __init__.py ← THIS distribution
└── reviewloop/ __init__.py ← ships from alissa-github-review-daemon
The rule: a distribution owns a leaf package and declares only that subtree
(find_namespace_packages(include=[PACKAGE, f"{PACKAGE}.*"])). Adding an
__init__.py at any namespace level would claim it for one distribution and
shadow the others.
Install
pip install -e ./alissa-tools-github-devloop
Console scripts
| Command | Entry point |
|---|---|
alissa-devloop |
alissa.tools.github.devloop.__main__:main |
Layout
src/main holds the package tree, src/test mirrors it as test_*. The
distribution version lives in the plain-text version file next to the module
it versions (src/main/alissa/tools/github/devloop/version), read by both
setup.py and version.py.
Account-level conditions
The prompt responder (prompts.py classifies, loop.py acts) treats four
pane conditions as the ACCOUNT's, not the worker's — nothing a keystroke can
answer: login_expired, auth_rejected (a 401), usage_limit and
out_of_credits. Each pages the operator once per kind per 6 h on the lane's
issue/PR and holds every new spawn while it stands (at most 1 h on one pane,
then kill-and-retry re-confirms) — except usage_limit, whose session is
never killed and whose hold is the ledger's (below).
Usage limits (issues #142, #144). usage_limit admits the window wordings
(You've hit your weekly limit, Usage limit reached, …), the "limit
reached … resets …" family (Claude usage limit reached. Your limit will reset at 5pm (America/New_York)., 5-hour limit reached ∙ resets 3pm,
Opus weekly limit reached ∙ resets Oct 6, 1pm) and the
MODEL-named one Claude Code prints now — You've reached your Fable limit. Run /usage-credits to continue or switch models with /model. (also Opus,
Sonnet, …): one capitalised model token, a known family name or any token
corroborated by that trailing hint on the same line. An over-limit account
answers the directive with that notice as the worker's ONLY assistant turn
and leaves the session idle at ❯, which the banner rule (never a ● row)
cannot see, so the first-turn limit check reads the responder's capture of
any young worker (develop-*, fix-*, maintain-*, revert-*, spawned by
this ledger inside the last stale_minutes): the notice, above an idle
prompt, with no other ● turn since the directive. A match
(source: "first_turn", matched: "model_limit", the model) is:
- held — the daemon-wide spawn hold, clocked on the ledger from the
newest limit (it survives a restart and a listing failure) and lifted only
when a spawn made AFTER the limit gets through its first turn: after 1 h
ONE probe spawn is admitted at a time (none while a probe is in flight,
none while another account notice holds; under
max_sessionsthe probe is judged without the kept limited sessions, which would otherwise fill the cap for good); a probe whose first turn is the notice again is refunded, re-arms the clock and is killed — it did no work, and keeping it would add one session per hour of the limit (pastmax_sessionstoo), so the live sessions stay at the ones the limit found at work plus at most the one probe in flight; the first probe that does real work lifts the hold (usage-limit-lifted:on the escalation ledger,escalation.usage_limit_liftedon the wire) and each kept limited session's lane gets a line saying to resume it; - paged — once per 6 h window, through the account-kind page;
- never killed (issue #144; a failed probe, above, is the one exception) —
a retry would hit the same wall, and the idle session is the lane's own work for the operator to resume once the
account is back (or
alissa tmux kill, to let the retry edge respawn); the wedge kills skip a limited session too; - refunded — the spawn row is stamped
limited_at, and everyattempt_capcount leaves limited rows out (numbering stays monotonic, so session names never repeat). An account over its limit never caps a lane.
A usage-limit BANNER on a working session (source: "pane") is held the same
way: its row is stamped (refunded), the hold is the ledger's, and the session
is never killed. The same notice after real worker output (a tool call, the
worker's own prose, a quoted tool row) stays worker output. Each limited
session is one usage.limited loop event: data: { kind: "usage_limit", source: "pane" | "first_turn", matched: "model_limit" | "usage_limit", model, resetsAt? } — resetsAt only when the notice states a reset, as an ISO-8601
UTC instant when that needs no guess (the legacy |<epoch> form, or a clock
time with an explicit zone) and as the stated phrase otherwise; no pane text,
no credential material.
The fix edge stops presuming. A stale fix round with no triage reply from the author used to be re-enqueued on the timer alone ("presumed dead"). It now reads the tmux roster first: ALIVE is in flight (the responder owns a live idle pane; a once-per-episode "Fix loop stalled?" ping floors the wait), INDETERMINATE defers one pass, and only DEAD respawns — "CONFIRMED dead", or, after a refunded limit, "previous session limited (usage limit; attempt refunded)". A kept limited session reads ALIVE, so no edge respawns over it.
The re-enqueue guard (issue #144)
The issue edge's respawn rule reads the linked-PR signal over every PR
state: GitHub.linked_pull_requests walks the timeline once and reports each
same-repo cross-referenced PR as open, merged or closed. When a stale
attempt's session is CONFIRMED dead with no open PR, a merged PR of the
daemon's own that closes the issue is SKIPPED "finished (PR #n merged)" and
a closed-unmerged one is SKIPPED "closed unmerged — orchestrator's page" —
never a respawn (provenance: token author and a closing keyword, fetched; an
unreadable candidate stands the respawn down). And _spawn re-reads the
issue immediately before every enqueue: a closed issue is SKIPPED "issue
closed between search and spawn", nothing is claimed, and the reserved slot
is handed back. The respawn-with-resume on an open draft PR is unchanged.
License
Licensed under the
Apache License, Version 2.0 — the
LICENSE and NOTICE files shipped with this distribution carry the full text
and the attribution and trademark terms. Both ship inside the sdist and the
wheel's .dist-info.
"Alissa" and "Fahera" are trademarks of CORE FAHERA ENTERPRISE HOLDINGS S. DE R.L. and are not covered by the license — the grant covers the code, not the names.
Metadata
Release files for alissa-tools-github-devloop 0.8.35
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| alissa_tools_github_devloop-0.8.35.tar.gz | 351.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| alissa_tools_github_devloop-0.8.35-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 713.9 kB
Release files / alissa_tools_github_devloop-0.8.35.tar.gz
| Download URL | alissa_tools_github_devloop-0.8.35.tar.gz |
|---|---|
| Size | 351.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fef31ef6e635756e43bf3daba32e6aa7fc64d050bb7d8b49532eb15683f0a67e
|
|
BLAKE2b-256 checksum How to use checksums |
4e785e133c35850ad442e257069feb8303b4e10016e8a0c9abc1f00ec6f25a73
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|
Release files / alissa_tools_github_devloop-0.8.35-py3-none-any.whl
| Download URL | alissa_tools_github_devloop-0.8.35-py3-none-any.whl |
|---|---|
| Size | 362.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ef41dbe31370f78778560b6fd0f85e6a73a9a4b3b8de9d0b2cea7e327231a36
|
|
BLAKE2b-256 checksum How to use checksums |
20cbda6c595adaafb9d04c3c9c9d38a08bbdda17a86e2807bf53fd746d6c9580
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.12
|