feat(pm-agent): an in-cluster AI Program Manager that turns Gitea issues into pull requests #57
No files matched your search
@@ -90,6 +90,12 @@ All offline, in the pinned toolbox (`./infra/devcontainer/dev.sh run`), never on
|
||||
kustomization, because the kustomization's comment named it by filename. Correctly
|
||||
refused: the distance between a filename in a comment and a line under `resources:` is
|
||||
one careless edit. The comment now points at it in prose.
|
||||
- **A self-review of the trigger found and fixed one real defect** before any of this ran:
|
||||
`last_activity()` compared RFC 3339 timestamp STRINGS to find the newest comment, which is
|
||||
only correct while every offset Gitea returns is identical — and the failure mode is the
|
||||
worst available one, the PM answering its own last comment in a loop. It now takes the last
|
||||
element of the comments list, which is the order the endpoint returns, so there is nothing
|
||||
about timezones to be right about.
|
||||
- **`check-jsonschema --schemafile schemas/servicedefinition.schema.json catalog/gitea.yaml`
|
||||
→ ok.**
|
||||
- **The golden reproduces:** `python3 catalog/render.py --all catalog -o apps --wave-layout
|
||||
|
||||
@@ -599,19 +599,22 @@ def render_thread(cfg: Config, issue: dict, comments, pr: dict, status: dict,
|
||||
|
||||
|
||||
def last_activity(issue: dict, comments) -> tuple:
|
||||
"""Who wrote the newest thing in this thread, and when.
|
||||
"""Who wrote the newest thing in this thread, and when. THE TRIGGER DEPENDS ON THIS.
|
||||
|
||||
Deliberately NOT `issue.updated_at`: that moves when a label is added or the title is
|
||||
edited, which would re-trigger the PM for a change nobody asked it about.
|
||||
|
||||
And deliberately NOT a comparison of the timestamp STRINGS. Gitea returns RFC 3339 with
|
||||
a timezone offset, and lexicographic order over such strings is only correct while every
|
||||
offset is identical — true today, silently wrong after a server timezone change, and the
|
||||
failure would be the PM answering its own last comment in a loop. The comments endpoint
|
||||
returns them in creation order, so the last element IS the newest; that is one less thing
|
||||
to be right about.
|
||||
"""
|
||||
actor = (issue.get("user") or {}).get("login")
|
||||
when = issue.get("created_at") or ""
|
||||
for c in comments:
|
||||
ts = c.get("created_at") or ""
|
||||
if ts >= when:
|
||||
when = ts
|
||||
actor = (c.get("user") or {}).get("login")
|
||||
return actor, when
|
||||
if comments:
|
||||
newest = comments[-1]
|
||||
return (newest.get("user") or {}).get("login"), newest.get("created_at") or ""
|
||||
return (issue.get("user") or {}).get("login"), issue.get("created_at") or ""
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- job
|
||||
|
||||
Reference in new issue
Block a user