Commit Graph

1627 Commits

Author SHA1 Message Date
Harshit Singh Bhandari 2b4af15605
Merge pull request #15 from aoagents/fix/lcm-writer-contract
fix: reconcile LCM writer contract
2026-05-29 17:00:46 +05:30
harshitsinghbhandari 1376b01933 fix: align writer contract with schema-2 2026-05-29 16:13:14 +05:30
harshitsinghbhandari fb7dbbbb42 fix: guard spawn initiation and restore metadata 2026-05-29 15:43:58 +05:30
neversettle 59a654afea
feat(backend): HTTP daemon skeleton (Phase 1a) — #10 (#14)
* feat(backend): HTTP daemon skeleton — config, health, runfile, graceful shutdown (#10)

Phase 1a of the Go HTTP daemon lane (#10). Stands up the loopback-only
sidecar skeleton the later REST/SSE/WS/static surfaces build on:

- config: env-driven (AO_HOST/PORT/ENV/timeouts/run-file) with zero-config
  defaults; binds 127.0.0.1:3001; validates and fails fast on bad input.
- httpd: chi router with the recoverer → request-id → logger → real-ip
  middleware stack and /healthz + /readyz probes. Per-request timeout is
  carried in config but intentionally not global — it scopes to /api/v1 in
  Phase 1b so it never throttles SSE/WS/health.
- runfile: atomic PID + port handshake (running.json) for the Electron
  supervisor, with a dead-PID stale check so a crashed predecessor doesn't
  block startup while a live one fails fast.
- server: bind-before-publish (port conflict fails fast), graceful shutdown
  on SIGINT/SIGTERM via signal.NotifyContext with a 10s hard timeout, and
  run-file cleanup on exit.

Why: the daemon must be safely supervisable as a child process — the
supervisor needs a discoverable PID/port and the daemon must not leave a
half-started process or stale handshake behind. Locking the lifecycle down
now keeps the future port split a small change rather than a rewrite.

Tests cover config defaults/overrides/validation, run-file round-trip and
live/dead PID detection, health probes, full Run lifecycle, and port-conflict
fail-fast.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(backend): drop Env config field — not needed yet (#10)

Per review on #14: AO_ENV / Config.Env / IsProduction() weren't load-bearing
for Phase 1a — they only switched the slog handler. Removing them now keeps
the surface minimal; the env knob can come back later when a real consumer
needs it.

- config: remove Env field, AO_ENV parsing, and IsProduction helper.
- main: collapse newLogger to a single text-handler path.
- httpd: drop the env field from the listening log line.
- tests: drop the env assertions and AO_ENV fixture.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs: add backend run + config quick-start to README (#10)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(backend): address Phase 1a review comments (#10)

- config: drop AO_HOST entirely — the daemon is loopback-only by design,
  so making the bind host env-configurable was a security footgun
- config: use net.JoinHostPort in Addr() so IPv6 literals stay valid
- config: reject zero/negative AO_REQUEST_TIMEOUT and AO_SHUTDOWN_TIMEOUT
  (time.ParseDuration accepts both; either would silently break the
  daemon — instant request expiry / no graceful drain)
- runfile: split processAlive into unix/windows build-tagged files so
  liveness detection is reliable on both platforms (Windows uses
  OpenProcess; POSIX keeps signal 0)
- runfile: document os.Rename overwrite semantics (atomic on POSIX,
  REPLACE_EXISTING on Windows) so the temp-then-rename pattern's
  cross-platform behaviour is explicit
- httpd tests: give probe/waitForHealth clients an explicit per-request
  timeout so a stalled connect can't hang the test on the outer deadline

* fix(backend): strip trailing blank line from runfile.go (#10)

gofmt CI was failing because removing the orphan processAlive doc
comment left an extra newline at EOF.

* fix(backend): cross-platform run-file replace + AO_HOST rationale (#10)

- runfile: introduce build-tagged atomicReplace — POSIX rename(2) on
  Unix, MoveFileEx with MOVEFILE_REPLACE_EXISTING on Windows. The Go
  runtime happens to do the Windows call internally already, but
  invoking it directly makes the cross-platform contract explicit
  instead of a runtime implementation detail
- runfile: tighten process_unix.go build tag from `!windows` to `unix`
  so plan9/js/wasm fail to build rather than silently using a broken
  signal-0 probe
- runfile: add TestWriteOverwritesExisting covering the stale run-file
  replace path that none of the previous tests exercised
- config: anchor the loopback-only decision in the LoopbackHost doc so
  the next contributor doesn't reintroduce AO_HOST without the security
  rationale

* fix(backend): route chi access logs through slog/stderr (#10)

chi's middleware.Logger writes via stdlib log to stdout, but the
daemon's slog logger writes to stderr — so REST traffic and daemon
logs landed on different streams in different formats. Replace it
with a small slog-backed requestLogger that:

- Wraps the response writer via middleware.NewWrapResponseWriter so
  status/bytes are accurate even when handlers return without an
  explicit WriteHeader.
- Reads the request id off the context set by middleware.RequestID
  (kept mounted just before this middleware so the id is available).
- Emits one structured Info line per request with method, path,
  status, bytes, duration, and remote — same key=value shape as the
  rest of the daemon, one stream for the Electron supervisor to
  capture.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 10:02:53 +05:30
harshitsinghbhandari 1fee0164e2 fix: restore revision monotonicity 2026-05-28 19:46:54 +05:30
harshitsinghbhandari 7160497013 add agent-orchestrator.yaml to gitignore 2026-05-28 19:37:20 +05:30
harshitsinghbhandari fcb3aec988 fix: handle restore rollback and spawn id collisions 2026-05-28 17:25:02 +05:30
harshitsinghbhandari e66988ab77 fix: reconcile lifecycle writer contract 2026-05-28 17:16:26 +05:30
Harshit Singh Bhandari 7afa332ade
Merge pull request #16 from aoagents/fix/scm-observer-seam
fix: handle SCM observer seam facts
2026-05-28 17:14:42 +05:30
Harshit Singh Bhandari 0ef3066312
Merge pull request #17 from aoagents/feat/zellij-runtime
feat: add zellij runtime adapter
2026-05-28 17:01:48 +05:30
harshitsinghbhandari 1e715ecfa9 style: gofmt zellij runtime adapter 2026-05-28 16:20:13 +05:30
harshitsinghbhandari db6975eb71 fix: tighten zellij runtime attach and launch 2026-05-28 16:18:27 +05:30
harshitsinghbhandari 586931b3ff feat: add zellij runtime adapter 2026-05-27 23:05:08 +05:30
harshitsinghbhandari f0766ebd8f fix: handle SCM observer seam facts 2026-05-27 22:19:07 +05:30
Harshit Singh Bhandari bbaf6650e3
Merge pull request #13 from aoagents/feat/draft-pr-state
feat: handle draft PR lifecycle state
2026-05-27 20:37:42 +05:30
harshitsinghbhandari 09a82d9206 fix: keep draft PR reactions active 2026-05-27 20:30:14 +05:30
harshitsinghbhandari 77c01daf42 feat: handle draft PR lifecycle state 2026-05-27 18:34:42 +05:30
Harshit Singh Bhandari f9bb4ce90d
Merge pull request #11 from aoagents/aa-8-tmux-worktree-adapters
feat: add tmux runtime adapter
2026-05-27 18:26:39 +05:30
Harshit Singh Bhandari 0f57a19dd7
Merge pull request #12 from aoagents/feat/9
Add git-worktree Workspace adapter
2026-05-27 18:21:13 +05:30
harshitsinghbhandari 37f1fe269e fix: harden tmux runtime teardown and ids 2026-05-27 18:20:03 +05:30
harshitsinghbhandari b5a344ac07 fix: use exact tmux targets 2026-05-27 17:17:59 +05:30
harshitsinghbhandari 9df3fb59bf feat: add git worktree workspace adapter 2026-05-27 17:12:40 +05:30
harshitsinghbhandari f975ca24c9 feat: add tmux runtime adapter 2026-05-27 16:58:03 +05:30
Harshit Singh Bhandari cdfd97cb98
Merge pull request #2 from aoagents/feat/lcm-sm-contracts
feat(backend): Lifecycle Manager + Session Manager lane
2026-05-27 14:28:52 +05:30
Harshit Singh Bhandari b44aca46a2
Merge pull request #8 from aoagents/fix/lcm-sm-review-comments
fix(session): harden teardown/restore safety + drop dead reaction flag
2026-05-27 14:24:35 +05:30
harshitsinghbhandari f03c7c8c88 fix(session): harden teardown/restore safety + drop dead reaction flag
Address PR #2 Copilot review comments on the merged LCM+SM lane:

- session: validate runtime handle + workspace path before Kill/Cleanup
  teardown; refuse (ErrIncompleteTeardownMetadata) or skip rather than
  hand empty args to a real adapter's Destroy (unsafe delete).
- session: reject Restore unless the session is terminal
  (ErrNotRestorable) so a live session can't spawn a duplicate
  runtime/workspace.
- ports: document SpawnConfig.OpenTerminal as reserved/not yet honored.
- lifecycle: remove the unread reactionConfig.auto field; note
  approved-and-green is notify-only (human decides to merge).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:21:47 +05:30
harshitsinghbhandari 1258a3ef14 docs: document the LCM + Session Manager lane (architecture + status)
Add docs/ for newcomers: an index, an architecture deep-dive (the
OBSERVE→DECIDE→ACT loop, the canonical state model, the package layout, every
component, and the load-bearing invariants), and a status/roadmap (what's done
PR-by-PR, what's left, the integration to-dos + carried-forward items, the open
cross-lane contract questions, and where to plug in). Link them from the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:08:13 +05:30
Harshit Singh Bhandari 6cf7c0400d
Merge pull request #7 from aoagents/session/aa-5
feat(session): Session Manager — spawn/kill/list/get/send/restore/cleanup
2026-05-27 14:03:11 +05:30
harshitsinghbhandari 4c331d907e test(session): cover spawn orphan-to-errored route and restore runtime rollback (review follow-ups)
Adds an injectable OnSpawnCompleted failure to the recording LCM and two tests:
- Spawn: when OnSpawnCompleted fails, the seeded record is parked terminal/errored
  (via OnKillRequested(KillError)) and runtime+workspace are torn down.
- Restore: when OnSpawnCompleted fails post-create, the new runtime is destroyed
  while the workspace is preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 14:00:44 +05:30
harshitsinghbhandari 162881d2bd fix(session): harden Restore — require agent session id, roll back runtime on post-create failure (PR #7 review)
- Restore now fails early with a clear error if MetaAgentSessionID is missing,
  rather than emitting an ambiguous "resume nothing" launch command (no stored
  prompt means a fresh-launch fallback isn't possible).
- On a post-runtime-create failure (reopen patch or OnSpawnCompleted), best-effort
  destroy the newly created runtime (never the workspace, which holds prior work)
  so we don't strand a live process while parking the session terminal.
- Added a test for Restore with a missing agent session id: errors early, touches
  no workspace/runtime, leaves the session terminal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:50:24 +05:30
harshitsinghbhandari 538210844e feat(session): implement Session Manager (spawn/kill/list/get/send/restore/cleanup)
Implements ports.SessionManager against fakes for the outbound ports. The SM is
the explicit-mutation half of the lane: it drives Runtime/Agent/Workspace, seeds
the initial lifecycle, and routes outcomes to the LCM (OnSpawnCompleted /
OnKillRequested). It never derives observed state and is the single producer of
the derived display status (attached on read, never persisted).

- Spawn: Workspace.Create -> Runtime.Create (AO_* identity env) -> Seed ->
  OnSpawnCompleted, with eager rollback of completed steps on failure.
- Kill: OnKillRequested first -> Runtime.Destroy -> Workspace.Destroy, honoring
  the worktree-remove safety (refusal surfaced, never forced).
- List/Get: derive status via DeriveLegacyStatus. Send: via AgentMessenger.
  Restore: re-seed (reopen) + relaunch via GetRestoreCommand. Cleanup: reclaim
  terminal sessions, skip worktrees holding uncommitted work.

Store-contract additions (co-owned with Tom's persistence layer, flagged for
review): LifecycleStore.Seed (explicit create-with-identity; OnSpawnCompleted
requires a seeded record) and LifecycleStore.Get (single record-with-identity
read; Load is lifecycle-only). Lifecycle test fake updated to satisfy both.

Tests route through the real LCM Manager (wrapped to record call order).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:43:40 +05:30
Harshit Singh Bhandari f70368b4ac
Merge pull request #6 from aoagents/session/aa-4
feat(lifecycle): ACT layer — reaction table + escalation engine (split B)
2026-05-27 13:17:00 +05:30
harshitsinghbhandari 5081cf794b fix(lifecycle): kill clears trackers, send-failure budget, inclusive escalation (Copilot review)
- OnKillRequested now clears the session's escalation trackers after a
  successful kill, so a later duration-based TickEscalations can't emit
  reaction.escalated for a dead session (dispatch is still skipped).
- sendToAgent rolls back the attempt (and firstAttemptAt when it set it) on a
  messenger.Send error, so undelivered messages don't march a reaction toward
  escalation — honoring "send failures retry next tick" (§4.3).
- Duration escalation now uses an inclusive boundary (>=) in both shouldEscalate
  and TickEscalations, so a 30m reaction escalates at exactly 30m instead of
  waiting for the next tick.

Tests: kill clears trackers + no post-kill escalation; repeated failed delivery
never escalates; duration escalation fires at exactly escalateAfter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:11:00 +05:30
harshitsinghbhandari b2161d5582 fix(lifecycle): clear ci-failed tracker on recovery/incident-over (PR #6 review)
Address review finding #1: the persistent ci-failed tracker leaked and could
stale-silence a future regression. It was only cleared when leaving the
ci-failed reaction AND incidentOver held at that moment — so a recovery to
another open-PR state (ci-failed -> approved -> merged) never cleared it.

- react() now clears ALL of a session's trackers when the state REACHED is
  incident-over (PR resolved / session terminal) OR a genuine recovery
  (approved/mergeable, which the open-PR ladder guarantees means CI is no
  longer failing). Keyed on the state reached, not the one left, since the
  recovery transition is typically review_pending->approved (empty beforeKey).
- Persistent ci-failed still survives the ambiguous review_pending limbo, so
  fail->pending->fail keeps one shared budget (§4.2).
- Document the out-of-lock react() dispatch caveat for the daemon integration
  step (review #2) and the intentionally-skipped agent-stuck 10m threshold.

Tests: re-arm after a genuine recovery (regression re-nudges, not silenced);
all session trackers cleared once the incident is over.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:26:45 +05:30
harshitsinghbhandari 8b8da8e6a4 feat(lifecycle): ACT layer — reaction table + escalation engine (split B)
Add the ACT half of the LCM: map persisted status transitions to reactions
(send-to-agent / notify / auto-merge) and drive escalation.

- reactions.go: the §4.2 default reaction table, reactionEventFor (mirrors
  DeriveLegacyStatus for the ACT layer), in-memory per-(session,reaction)
  escalation trackers, the react() dispatch chokepoint, and a real
  TickEscalations for duration-based escalations the synchronous LCM can't
  wake itself for. auto-merge action exists but is off by default;
  bugbot-comments/merge-conflicts are configured but dormant (no decide-core
  producer yet).
- manager.go: mutate now returns a transition; each Apply* path fires the
  mapped reaction after persist via the single synchronous react() seam.
  OnKillRequested intentionally does not react (explicit kill != inferred
  event). Split-A load->decide->diff->persist behavior is unchanged.

ci-failed budget is persistent across fail->pending->fail oscillation;
non-persistent trackers reset when the status leaves the triggering state.
Escalation silences further auto-dispatch until the condition clears.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 02:17:51 +05:30
Harshit Singh Bhandari 347eb1694d
Merge pull request #5 from aoagents/session/aa-3
feat(lifecycle): LCM Apply* pipeline (split A)
2026-05-27 01:52:27 +05:30
harshitsinghbhandari 9eb5348604 feat(lifecycle): activity resolves detecting + review polish
Address Harshit's PR #5 review (approve w/ design confirms + polish):

- #1 (design decision): a valid activity signal is proof of life, so it now
  resolves a detecting session — writes the activity-mapped session state and
  clears the quarantine memory. Scoped to detecting only; a liveness-escalated
  stuck stays the probe pipeline's to resolve. Terminal still never reopens.
- #2: document why a merged/closed PR parks the session axis even over an
  activity-owned needs_input/blocked (a merge is a milestone), unlike the
  open-PR path that defers to activity.
- #3: map plain idle activity to a neutral session reason instead of the
  misleading research_complete (kept for ready, which implies completion).
- #6: cover all three kill kinds (manual/cleanup/error), the open-PR review
  branches (changes_requested/mergeable/review_pending), and the neutral idle
  reason. Coverage 86.5% -> 88.6%.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 01:49:28 +05:30
harshitsinghbhandari 3945b10f20 fix(lifecycle): address PR #5 review
- shouldWriteSessionRuntime: never resurrect a terminal session; an
  observation may refresh the runtime axis but must touch neither the
  session axis nor the detecting memory (gated in ApplyRuntimeObservation).
- OnSpawnCompleted: error on an unseeded session instead of fabricating a
  partial record (SM must seed first — a missing seed is a contract violation).
- OnKillRequested: no-op on an unknown/already-gone session (benign race)
  instead of fabricating a terminal record.
- keyedMutex: reference-count entries and evict on last release so the lock
  map stays bounded in a long-running daemon.
- runtimeSubstateFromFacts: map RuntimeProbeIndeterminate to RuntimeUnknown
  with a neutral reason, distinct from the probe_error of a failed probe.

Adds tests for terminal non-resurrection, unseeded spawn-completed error,
and unknown-session kill no-op.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 01:37:14 +05:30
harshitsinghbhandari 1420bb9493 feat(lifecycle): implement LCM Apply* pipeline (split A)
Implements ports.LifecycleManager as a synchronous observe->decide->persist
reducer. Every entrypoint runs the shared pipeline under a per-session lock:
load canonical -> run the matching pure decider -> diff into a sparse
merge-patch -> persist. Never polls, never writes the display status.

- ApplyRuntimeObservation -> probe decider; always writes the runtime axis.
- ApplySCMObservation -> open-PR / terminal-PR deciders (failed fetch is a
  no-op: failed probe != "no PR"). Open PRs write only the PR axis.
- ApplyActivitySignal -> updates the activity axis + maps onto the session
  axis; only valid-confidence signals are authoritative.
- OnSpawnCompleted -> runtime alive + handles to metadata; session stays
  not_started (display: spawning).
- OnKillRequested -> SM's explicit terminal-write authority.
- TickEscalations -> no-op stub (reaction/escalation engine is split B).

Composition rule (#1): liveness owns the runtime + death axis; activity owns
the working/idle/waiting axis. A healthy probe verdict writes the session axis
only to recover a liveness-owned state, so it never clobbers an activity-owned
needs_input/blocked. Activity is the mirror: it stays off the death axis.

Detecting clear (#3): a non-detecting probe verdict clears stale detecting
memory so the next probe reads no phantom Prior.

Built/tested against in-memory fakes (LifecycleStore with full merge-patch +
ExpectedRevision, recording Notifier/AgentMessenger). Per-session
serialisation verified under -race.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 01:26:15 +05:30
Harshit Singh Bhandari 217c50eeb3
Merge pull request #4 from aoagents/session/aa-2
feat(decide): pure DECIDE core + exhaustive truth-table tests
2026-05-27 00:42:59 +05:30
harshitsinghbhandari 918c5b4b3a feat(decide): populate open-PR Evidence; document decision zero-value contract
Address Copilot review:
- ResolveOpenPRDecision now sets a stable, timestamp-free Evidence summary
  "<condition> #<num> <url>" for every ladder outcome, consuming the
  previously-unused OpenPRInput.Number/URL identity inputs and making PR
  decisions traceable in logs. Covered by TestResolveOpenPRDecisionEvidence.
- Document LifecycleDecision's zero-value contract: an empty PRState/PRReason
  means "this decider does not address PR — leave unchanged", not PRNone. The
  LCM must map empty PR fields to a nil LifecyclePatch.PR; writing PRNone on a
  probe tick would clobber a live PR. (Pointers were considered but the empty
  sentinel is distinguishable from every valid state and consistent with the
  codebase's value-enum style; LifecyclePatch already owns nil-means-unchanged.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:38:34 +05:30
harshitsinghbhandari cdfcdd2def style(decide): gofmt the timestampPatterns doc block
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:26:46 +05:30
harshitsinghbhandari fbfbcd5f1b docs(decide): document each timestampPatterns regex with examples
Clarify the timestamp-stripping block flagged in review: spell out what
each of the three regexes matches (full ISO/RFC3339 datetime, bare
time-of-day, bare unix epoch) and why order matters.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:26:33 +05:30
harshitsinghbhandari 4d8a20676a refactor(decide): split type definitions into types.go; clarify detecting helper
Address PR review (aa-1):
- Move the decider input/output type definitions (LifecycleDecision,
  ProbeInput, ProcessLiveness, OpenPRInput, DetectingInput) out of the
  execution file into a dedicated types.go, leaving decide.go for behaviour.
- Expand the detecting() helper doc to spell out that it adapts a probe
  verdict into the shared CreateDetectingDecision anti-flap path so each
  probe branch doesn't re-implement the quarantine counter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:25:44 +05:30
harshitsinghbhandari 9920c6daaa fix(decide): reach mergeable without formal approval; broaden consistency test
Address PR review (aa-1):
- ResolveOpenPRDecision now keys MERGEABLE on Mergeable alone (checked
  before Approved). Mergeability is the authoritative merge gate, so a PR
  on a no-required-review repo no longer falls through to PR_OPEN. The
  approved+mergeable and approved-only cases are unchanged.
- Broaden the derive-consistency test to cover the probe and terminal
  deciders too, not just the open-PR ladder.
- Document the HashEvidence epoch-stripping regex's breadth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:06:57 +05:30
harshitsinghbhandari 6e90734276 feat(decide): implement pure DECIDE core with exhaustive truth-table tests
Replace the stubbed deciders in domain/decide with real, total,
side-effect-free implementations:

- ResolveProbeDecision: kill-intent short-circuits to terminal; failed
  probes and probe disagreement route to detecting; only runtime-dead +
  process-dead + no-recent-activity concludes killed.
- ResolveOpenPRDecision: the PR pipeline ladder
  (ci_failing > changes_requested > approved+mergeable > approved >
  review_pending > idle-beyond > open).
- ResolveTerminalPRStateDecision: merged -> idle/merged_waiting_decision,
  closed -> idle.
- CreateDetectingDecision: anti-flap quarantine — unchanged-evidence
  counter with StartedAt preserved across the episode so the duration cap
  is a real wall-clock safety net; escalates to stuck at 3 ticks or 5m.
- HashEvidence: strips timestamps/epochs and collapses whitespace before
  hashing so restamped-but-unchanged signals compare equal.

Table tests cover every branch (100% statement coverage), including a
consistency check that the open-PR ladder's display Status matches
DeriveLegacyStatus over the canonical state it emits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 23:54:55 +05:30
harshitsinghbhandari d824c4f767 ci: add Go build/test and gitleaks secret-scan workflows
- go.yml: gofmt check, build, vet, and race-enabled tests for backend/,
  triggered on backend changes and pushes to main.
- gitleaks.yml: secret scanning on PRs and main using gitleaks-action v1
  (license-free; v2 requires GITLEAKS_LICENSE for org repos).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 21:02:58 +05:30
harshitsinghbhandari e8f60d0b27 refactor(backend): address contract review on LCM+SM ports
Review feedback on PR #2:

- Add CanonicalSessionLifecycle.Revision (monotonic write counter) distinct
  from the schema Version; LifecyclePatch.ExpectedVersion -> ExpectedRevision
  now compares it, so optimistic locking actually works.
- LifecycleStore.List returns []domain.SessionRecord (persistence shape, no
  derived status); add SessionRecord and make Session embed it. Keeps the
  Session Manager the single producer of the derived display status.
- SpawnOutcome.RuntimeHandle is now the structured ports.RuntimeHandle, not a
  string, so Destroy/SendMessage get the handle without ad-hoc encoding.
- Agent.IsProcessRunning -> ProbeProcess returning ProcessProbe (liveness),
  not domain.ActivityState; the name no longer implies a boolean.
- Document LifecyclePatch Detecting vs ClearDetecting precedence (clear wins).
- Correct the DeriveLegacyStatus doc: hard session states outrank PR facts;
  "PR dominates" applies only to the soft idle/working states. Implementation
  was already correct (matches canonical AO); only the comment overstated it.
- Replace personal-name attributions in package/interface comments with
  role-based terms (SCM poller / persistence adapter / API layer).

go build / go vet / go test all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:55:27 +05:30
harshitsinghbhandari d630319f04 feat(backend): LCM + Session Manager contract package (domain + ports)
Contract-first boundary for the Lifecycle Manager + Session Manager lane.
Pure shapes only — types, interfaces, and the display-status derivation —
so adil (SCM poller), Tom (persistence), and aditi (API) can review and
build against a stable boundary before any behaviour lands.

domain/
  - CanonicalSessionLifecycle: the only persisted state (session/pr/runtime
    sub-states), with Activity + Detecting sub-states added as decider inputs
    that must survive between observations.
  - DeriveLegacyStatus: the sole producer of the derived display status
    (never persisted), with 11 table tests.
ports/
  - inbound: LifecycleManager (Apply* pipeline, per-session serialised) and
    SessionManager.
  - outbound: LifecycleStore (Tom), Notifier, AgentMessenger, and the
    Runtime/Agent/Workspace plugin ports (co-owned with the agents lane).
  - facts: SCMFacts / RuntimeFacts / ActivitySignal DTOs.
decide/ pure-core signatures + I/O types; bodies stubbed for the next PR.

Folds in four design-review fixes (documented in-code, pending team confirm):
  1. Activity + Detecting persisted so the pure decider has memory across calls.
  2. Per-session serialisation documented; LifecyclePatch.ExpectedVersion
     offers optimistic-locking as an alternative.
  3. LifecyclePatch is a sparse pointer-field merge-patch (+ ClearDetecting).
  4. SCMFacts gains Fetched (failed fetch != "PR closed") and per-comment
     IsBot (bot vs human route to different reactions).

go build / go vet / go test all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 20:00:31 +05:30
Harshit Singh Bhandari 21564cda2c
Merge pull request #1 from aoagents/feat/repo-skeleton
chore: scaffold backend/ and frontend/ skeletons for rewrite
2026-05-26 15:27:38 +05:30