Category: spec-conformance Severity: blocker
Location: server/session.go:604-616
Spec: ARCP v1.1 §7.4
What
Spec §7.4 reserves cancellation for 'the submitting session'; §7.7 documents that Resume 'Carries cancel authority: Yes'. Authorization here compares the live *session pointer (job.session != s); after resume, server.handshake builds a new *session struct with the same session_id, but job.session still points to the dead struct created at submit time. Any cancel attempted on the resumed connection is rejected with PERMISSION_DENIED, breaking resume-then-cancel.
Evidence
func (s *session) handleJobCancel(env arcp.Envelope) error {
var req messages.JobCancel
_ = env.DecodePayload(&req)
job := s.srv.lookupJob(env.JobID)
if job == nil { return s.sendError(arcp.CodeJobNotFound, "job "+env.JobID+" not found") }
if job.session != s { return s.sendError(arcp.CodePermissionDenied, "only the submitting session can cancel") }
job.cancelWithReason(req.Reason)
return nil
}
Proposed fix
Authorize cancellation by comparing the session id and principal recorded on the job (e.g., job.sessionID == s.id && job.principal == s.principal), not the *session pointer; on successful resume, also re-point job.session to the live session so outbound sends route through the new outbox.
Acceptance criteria
Category: spec-conformance Severity: blocker
Location:
server/session.go:604-616Spec: ARCP v1.1 §7.4
What
Spec §7.4 reserves cancellation for 'the submitting session'; §7.7 documents that Resume 'Carries cancel authority: Yes'. Authorization here compares the live *session pointer (job.session != s); after resume, server.handshake builds a new *session struct with the same session_id, but job.session still points to the dead struct created at submit time. Any cancel attempted on the resumed connection is rejected with PERMISSION_DENIED, breaking resume-then-cancel.
Evidence
Proposed fix
Authorize cancellation by comparing the session id and principal recorded on the job (e.g., job.sessionID == s.id && job.principal == s.principal), not the *session pointer; on successful resume, also re-point job.session to the live session so outbound sends route through the new outbox.
Acceptance criteria