|
@@ -113,6 +113,10 @@ def run(ctx):
|
|
|
"the payload's understanding is the project's CURRENT revision")
|
|
"the payload's understanding is the project's CURRENT revision")
|
|
|
|
|
|
|
|
# A giant revision is head-clamped at 16000 chars (the payload cap).
|
|
# A giant revision is head-clamped at 16000 chars (the payload cap).
|
|
|
|
|
+ # created_at goes in as a raw epoch INTEGER like every other bundle
|
|
|
|
|
+ # fixture: the ORM materialises DateTime columns from int64 (a text
|
|
|
|
|
+ # value in a DATETIME column fails the element conversion, which
|
|
|
|
|
+ # the project page read surfaces as a 500).
|
|
|
conn = db()
|
|
conn = db()
|
|
|
conn.execute("insert into project_understandings"
|
|
conn.execute("insert into project_understandings"
|
|
|
" (project_id, body, commit_hash, task_id, created_at)"
|
|
" (project_id, body, commit_hash, task_id, created_at)"
|
|
@@ -149,3 +153,146 @@ def run(ctx):
|
|
|
"dispatcher pushes the no-understanding task")
|
|
"dispatcher pushes the no-understanding task")
|
|
|
check(fake.start_payloads[-1].get("understanding") == "",
|
|
check(fake.start_payloads[-1].get("understanding") == "",
|
|
|
"a project without an understanding ships an empty string")
|
|
"a project without an understanding ships an empty string")
|
|
|
|
|
+
|
|
|
|
|
+ print("== understanding: the project page section ==")
|
|
|
|
|
+ # 'plain' has no understanding: empty current row, empty history,
|
|
|
|
|
+ # but the refresh action is authored.
|
|
|
|
|
+ plain_state = slot_public(auth, "/projects/%d" % plain_id, "project")
|
|
|
|
|
+ check(plain_state.get("refresh_understanding") is not None,
|
|
|
|
|
+ "the project slot authors the refresh action")
|
|
|
|
|
+ check((plain_state.get("understanding_current") or {}).get("body", "x") == "",
|
|
|
|
|
+ "a project without an understanding shows an empty current row")
|
|
|
|
|
+ check(plain_state.get("understanding_history") == [],
|
|
|
|
|
+ "a project without an understanding shows no history rows")
|
|
|
|
|
+ # 'demo' (project 1) has three revisions by now (two API inserts +
|
|
|
|
|
+ # the giant direct row): current is the giant, history is the two
|
|
|
|
|
+ # API revisions newest first.
|
|
|
|
|
+ demo_state = slot_public(auth, "/projects/1", "project")
|
|
|
|
|
+ current_row = demo_state.get("understanding_current") or {}
|
|
|
|
|
+ check(current_row.get("body", "").startswith("giant!"),
|
|
|
|
|
+ "the page's current understanding is the newest revision")
|
|
|
|
|
+ check(current_row.get("commit_hash") == "big0000"
|
|
|
|
|
+ and current_row.get("has_commit") is True,
|
|
|
|
|
+ "the current row carries the commit hash stamp flag")
|
|
|
|
|
+ history = demo_state.get("understanding_history") or []
|
|
|
|
|
+ check([h["commit_hash"] for h in history] == ["def2000", "abc1000"],
|
|
|
|
|
+ "the history lists the older revisions newest first (current not repeated)")
|
|
|
|
|
+ check(history[0]["body"].endswith("Second revision.")
|
|
|
|
|
+ and history[1]["body"].endswith("First revision."),
|
|
|
|
|
+ "history rows keep their markdown bodies")
|
|
|
|
|
+
|
|
|
|
|
+ print("== understanding: Refresh spins up the special task ==")
|
|
|
|
|
+ # The 'plain' project: no notes, no understanding — a first refresh.
|
|
|
|
|
+ result = act(auth, plain_state["refresh_understanding"], {})
|
|
|
|
|
+ check(any(d.get("type") == "notify" and d.get("kind") == "success" for d in result),
|
|
|
|
|
+ "the first refresh notifies success")
|
|
|
|
|
+ rows = db_rows("select id, title, status, mode, kind, description from tasks"
|
|
|
|
|
+ " where title like 'Understanding refresh: plain'")
|
|
|
|
|
+ check(len(rows) == 1, "refresh created exactly one task")
|
|
|
|
|
+ refresh_id = rows[0][0]
|
|
|
|
|
+ check(rows[0][2] == "queued" and rows[0][3] == "investigate"
|
|
|
|
|
+ and rows[0][4] == "task",
|
|
|
|
|
+ "the refresh task is queued, investigate mode, kind task (%s)" % (rows[0][2:5],))
|
|
|
|
|
+ brief = rows[0][5] or ""
|
|
|
|
|
+ check("none — first refresh" in brief,
|
|
|
|
|
+ "a first refresh embeds 'none — first refresh' as the previous hash")
|
|
|
|
|
+ check("Previous document:" not in brief,
|
|
|
|
|
+ "a first refresh carries no previous document")
|
|
|
|
|
+ check("save_understanding" in brief and "git rev-parse HEAD" in brief,
|
|
|
|
|
+ "the brief mandates the save_understanding finishing move")
|
|
|
|
|
+ check("do not edit any files" in brief,
|
|
|
|
|
+ "the brief states the investigate-mode no-edit constraint")
|
|
|
|
|
+ # A system comment marks the refresh on the task thread.
|
|
|
|
|
+ conn = db()
|
|
|
|
|
+ marker = conn.execute(
|
|
|
|
|
+ "select count(*) from task_comments where task_id = ? and author = 'system'"
|
|
|
|
|
+ " and body like 'Understanding refresh queued%'",
|
|
|
|
|
+ (refresh_id,)).fetchone()[0]
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+ check(marker == 1, "the refresh task carries the system comment marker")
|
|
|
|
|
+
|
|
|
|
|
+ print("== understanding: a live refresh refuses a second ==")
|
|
|
|
|
+ plain_state = slot_public(auth, "/projects/%d" % plain_id, "project")
|
|
|
|
|
+ refused = act(auth, plain_state["refresh_understanding"], {})
|
|
|
|
|
+ check(any(d.get("type") == "notify" and d.get("kind") == "error"
|
|
|
|
|
+ and "already running" in (d.get("message") or "") for d in refused),
|
|
|
|
|
+ "a second refresh while the task is live refuses")
|
|
|
|
|
+ rows = db_rows("select count(*) from tasks"
|
|
|
|
|
+ " where title like 'Understanding refresh: plain'")
|
|
|
|
|
+ check(rows[0][0] == 1, "the refused refresh created no second task")
|
|
|
|
|
+
|
|
|
|
|
+ print("== understanding: refresh against an existing document ==")
|
|
|
|
|
+ # The demo project has a current understanding + project notes:
|
|
|
|
|
+ # the brief must embed the old hash, the old document and the notes.
|
|
|
|
|
+ conn = db()
|
|
|
|
|
+ conn.execute("insert into project_notes (project_id, source_task_id, body,"
|
|
|
|
|
+ " created_at) values (1, 0, ?, strftime('%s','now'))",
|
|
|
|
|
+ ("Demo note one: the test harness lives in test/.",))
|
|
|
|
|
+ conn.execute("insert into project_notes (project_id, source_task_id, body,"
|
|
|
|
|
+ " created_at) values (1, 0, ?, strftime('%s','now') + 1)",
|
|
|
|
|
+ ("Demo note two: meson builds everything.",))
|
|
|
|
|
+ conn.commit()
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+ demo_state = slot_public(auth, "/projects/1", "project")
|
|
|
|
|
+ result = act(auth, demo_state["refresh_understanding"], {})
|
|
|
|
|
+ check(any(d.get("type") == "notify" and d.get("kind") == "success" for d in result),
|
|
|
|
|
+ "refreshing a project with an understanding succeeds")
|
|
|
|
|
+ row = db_rows("select id, description from tasks"
|
|
|
|
|
+ " where title like 'Understanding refresh: demo'")[0]
|
|
|
|
|
+ demo_brief = row[1]
|
|
|
|
|
+ check("Previous commit hash: big0000" in demo_brief,
|
|
|
|
|
+ "the brief embeds the previous document's commit hash")
|
|
|
|
|
+ check("```markdown" in demo_brief and "giant!" in demo_brief,
|
|
|
|
|
+ "the brief embeds the previous document in a markdown fence")
|
|
|
|
|
+ check("git log" in demo_brief and "..HEAD" in demo_brief,
|
|
|
|
|
+ "the brief directs the git-history review since the old hash")
|
|
|
|
|
+ check("Project notes from other agents:" in demo_brief
|
|
|
|
|
+ and "Demo note one: the test harness lives in test/." in demo_brief
|
|
|
|
|
+ and "Demo note two: meson builds everything." in demo_brief,
|
|
|
|
|
+ "the brief embeds every project note")
|
|
|
|
|
+
|
|
|
|
|
+ print("== understanding: the refresh task runs, saves, refresh works again ==")
|
|
|
|
|
+ # The fake saddle picks the queued plain refresh up (it asks when
|
|
|
|
|
+ # idle), runs it to done, and the understanding lands via the API.
|
|
|
|
|
+ check(wait_for(lambda: any(
|
|
|
|
|
+ int(p.get("task", {}).get("id") or 0) == refresh_id
|
|
|
|
|
+ for p in fake.start_payloads) or False,
|
|
|
|
|
+ timeout=30, what="refresh task assignment") is not None,
|
|
|
|
|
+ "the dispatcher assigns the refresh task to the saddle")
|
|
|
|
|
+ fake.push_results("done", summary="explored and saved the understanding")
|
|
|
|
|
+ check(wait_task_done(auth, refresh_id, timeout=30) is not None,
|
|
|
|
|
+ "the refresh task completes")
|
|
|
|
|
+ status, body = saddle_api("POST", "/api/saddle/understanding", fake_token,
|
|
|
|
|
+ {"project": "plain",
|
|
|
|
|
+ "body": "# Plain understanding\n\nFirst map.",
|
|
|
|
|
+ "commit_hash": "fff3000",
|
|
|
|
|
+ "task_id": refresh_id})
|
|
|
|
|
+ check(status == 200 and body.get("ok") is True,
|
|
|
|
|
+ "the completed refresh saves its understanding via the API")
|
|
|
|
|
+ plain_state = slot_public(auth, "/projects/%d" % plain_id, "project")
|
|
|
|
|
+ current_row = plain_state.get("understanding_current") or {}
|
|
|
|
|
+ check(current_row.get("body") == "# Plain understanding\n\nFirst map."
|
|
|
|
|
+ and current_row.get("commit_hash") == "fff3000",
|
|
|
|
|
+ "the page shows the new current understanding after the save")
|
|
|
|
|
+ check(plain_state.get("understanding_history") == [],
|
|
|
|
|
+ "the saved revision is current, not duplicated into history")
|
|
|
|
|
+ # Terminal refresh task: a new refresh is allowed again.
|
|
|
|
|
+ result = act(auth, plain_state["refresh_understanding"], {})
|
|
|
|
|
+ check(any(d.get("type") == "notify" and d.get("kind") == "success" for d in result),
|
|
|
|
|
+ "after the refresh task completes a new refresh is allowed")
|
|
|
|
|
+ rows = db_rows("select count(*) from tasks"
|
|
|
|
|
+ " where title like 'Understanding refresh: plain'")
|
|
|
|
|
+ check(rows[0][0] == 2, "the second refresh created its task")
|
|
|
|
|
+ # The new live task blocks again, and the new current doc is now
|
|
|
|
|
+ # the previous one in the fresh brief.
|
|
|
|
|
+ plain_state = slot_public(auth, "/projects/%d" % plain_id, "project")
|
|
|
|
|
+ refused = act(auth, plain_state["refresh_understanding"], {})
|
|
|
|
|
+ check(any(d.get("type") == "notify" and d.get("kind") == "error"
|
|
|
|
|
+ and "already running" in (d.get("message") or "") for d in refused),
|
|
|
|
|
+ "the new live refresh blocks a third")
|
|
|
|
|
+ row = db_rows("select description from tasks"
|
|
|
|
|
+ " where title like 'Understanding refresh: plain'"
|
|
|
|
|
+ " order by id desc")[0]
|
|
|
|
|
+ check("Previous commit hash: fff3000" in (row[0] or "")
|
|
|
|
|
+ and "First map." in (row[0] or ""),
|
|
|
|
|
+ "the refreshed brief embeds the new previous hash and document")
|