feat: Phase 9 gaps closed — ApprovalService.Decide convergence, execlog fold, execworker poller
Some checks are pending
ci / build-test (push) Waiting to run
ci / docker-build (push) Waiting to run

- ApprovalService (core/app/approval.go) + ApprovalRepo (postgres adapter) with
  full decide transaction: HMAC token verify, approval flip, execution un-gate,
  session-scoped window keys (+session suffix matching GovernanceStore gate),
  nomos session flip, audit+event on failure abort. httpapi DecideApproval now
  a thin presenter delegating to the service. ListPending payload format fixed
  (json.Unmarshal not raw-wrap).
- execlog folded into postgres adapter: internal/execlog deleted, NewExecutionLog
  / ReadExecutionLog live in the db package, callers updated (mcp, httpapi).
- execworker poller over ExecutionService.DispatchQueued: advisory lock leak
  fixed (defer/recover per execution), correlation_id preserved via Finalize
  event emission (ExecRunRepo.Finalize now emits execution.{status} with
  correlation_id from the row).
- Phase 8 session export-rename completed: Store, New, and all 53 methods
  exported; cmd/nomos/ agent.go fixed to use session.PendingContinuation etc.
- Coverage gates: ExecutionService.Submit 93.1%, PolicyService.Decide 100%.
- Plans index updated, VERSION bumped to 0.36.0.
This commit is contained in:
2026-08-16 12:29:59 +02:00
parent 986937799a
commit b98d7c24bf
27 changed files with 1161 additions and 600 deletions

View File

@@ -11,8 +11,8 @@ func TestIsAssent_Positive(t *testing.T) {
"ok go ahead and run it",
}
for _, c := range cases {
if !isAssent(c) {
t.Errorf("isAssent(%q) = false, want true", c)
if !IsAssent(c) {
t.Errorf("IsAssent(%q) = false, want true", c)
}
}
}
@@ -24,8 +24,8 @@ func TestIsAssent_Negative(t *testing.T) {
"maybe later", "",
}
for _, c := range cases {
if isAssent(c) {
t.Errorf("isAssent(%q) = true, want false", c)
if IsAssent(c) {
t.Errorf("IsAssent(%q) = true, want false", c)
}
}
}
@@ -38,8 +38,8 @@ func TestIsAssent_NegationBeatsAssentWord(t *testing.T) {
"wait, not yet please",
}
for _, c := range cases {
if isAssent(c) {
t.Errorf("isAssent(%q) = true, want false (negation should block)", c)
if IsAssent(c) {
t.Errorf("IsAssent(%q) = true, want false (negation should block)", c)
}
}
}
@@ -47,7 +47,7 @@ func TestIsAssent_NegationBeatsAssentWord(t *testing.T) {
// TestIsAssent_WholeWordBoundary regression-tests a real false positive found
// live: the old substring check matched "yes" inside "yesterday" (and would
// equally match "confirm" inside "confirmed"/"unconfirmed" for
// isTypedConfirmation below) because only negation used a word-boundary
// IsTypedConfirmation below) because only negation used a word-boundary
// check — assent/confirm words used a bare strings.Contains. Confirmed via a
// throwaway probe before being fixed; kept here permanently so a future
// change can't silently reintroduce it.
@@ -57,14 +57,14 @@ func TestIsAssent_WholeWordBoundary(t *testing.T) {
"my eyesight isn't great, what does that say",
}
for _, c := range cases {
if isAssent(c) {
t.Errorf("isAssent(%q) = true, want false (word-boundary: 'yes' must not match inside 'yesterday'/'eyesight')", c)
if IsAssent(c) {
t.Errorf("IsAssent(%q) = true, want false (word-boundary: 'yes' must not match inside 'yesterday'/'eyesight')", c)
}
}
}
// TestIsTypedConfirmation_ContractedNegation regression-tests the other real
// false positive: isTypedConfirmation gates DESTRUCTIVE actions, and
// false positive: IsTypedConfirmation gates DESTRUCTIVE actions, and
// "confirm" matching inside "confirmed" combined with contracted negatives
// ("haven't") not being in negationWords meant a message that explicitly
// says the operator has NOT confirmed something could read as confirming it.
@@ -75,8 +75,8 @@ func TestIsTypedConfirmation_ContractedNegation(t *testing.T) {
"we can't confirm that until tomorrow",
}
for _, c := range cases {
if isTypedConfirmation(c) {
t.Errorf("isTypedConfirmation(%q) = true, want false (contracted negation should block)", c)
if IsTypedConfirmation(c) {
t.Errorf("IsTypedConfirmation(%q) = true, want false (contracted negation should block)", c)
}
}
}
@@ -89,8 +89,8 @@ func TestIsTypedConfirmation(t *testing.T) {
"yes I confirm",
}
for _, c := range positive {
if !isTypedConfirmation(c) {
t.Errorf("isTypedConfirmation(%q) = false, want true", c)
if !IsTypedConfirmation(c) {
t.Errorf("IsTypedConfirmation(%q) = false, want true", c)
}
}
negative := []string{
@@ -98,8 +98,8 @@ func TestIsTypedConfirmation(t *testing.T) {
"no, don't confirm yet", "wait", "",
}
for _, c := range negative {
if isTypedConfirmation(c) {
t.Errorf("isTypedConfirmation(%q) = true, want false (only explicit confirm should pass)", c)
if IsTypedConfirmation(c) {
t.Errorf("IsTypedConfirmation(%q) = true, want false (only explicit confirm should pass)", c)
}
}
}