Skip to content

feat(org): submit — accept a supervisor, the one safe self-signed terms change - #276

Open
itsHabib wants to merge 2 commits into
mainfrom
feat/org-recharter
Open

feat(org): submit — accept a supervisor, the one safe self-signed terms change#276
itsHabib wants to merge 2 commits into
mainfrom
feat/org-recharter

Conversation

@itsHabib

Copy link
Copy Markdown
Owner

Why

checkTakeover admits a displacement only from a role the charter names in
Terms.Supervisors. Every lane in the live tenant names only human:mh, so
the operator is the only party who can clear a seat held by a dead session —
which is precisely the page a supervisor lane exists to prevent. Tonight a
desktop crash left two seats held by a stopped session and the only available
move was to ask the operator.

Why not a general recharter

FOLLOWUPS already answers this, and I hit it mid-build: a general verb was
written and withdrawn from #272. checkRecharter verifies only that
min_reader is monotone and checkWriter accepts the holder's own
incarnation, so exposing the whole kind would let a role widen its scope, lift
its ceilings, add effect classes, or drop the supervisors that may take it
over — all self-signed. Unchanged, and this PR does not touch it.

Why this slice is admissible

Under that entry's own criteria, not in spite of them. It names supervisors
as one of three attenuations a kernel can verify ("no shrink"), and adding
one is monotone in the accountability direction: it strictly grows the set of
roles that may displace you and cannot widen your own authority. None of the
five self-signed widenings the entry lists are reachable through it.

The guard is total rather than partial — every other term is carried through
byte-identical, including Tier, which is inert since #275 but still projected
into every digest.

And who writes it matters as much as what it says: recharter is admitted only
from Held or Active under the writer's own incarnation, so a role submits
to oversight while sitting in its own seat
. Consent, not imposition. Nothing
can be made supervisable from outside, which is the property that keeps this
from quietly becoming the parent-authority mechanism the kernel still lacks.

Validation

  • go build ./..., go vet, gofmt clean; go test ./cmd/org/... ./contracts/org/... all pass.
  • Three tests pin the property, not the happy path:
    • the whole of Terms survives a submit byte-identical (scope, effect
      classes, ceilings, retire) — a general recharter would have reset them to
      flag defaults;
    • takeover is refused with not_supervisor before submit and admitted
      after — the actual reason the verb exists;
    • re-submitting an existing supervisor writes no record.
  • cmd/org/CLAUDE.md and AGENTS.md updated and verified byte-identical for
    the CI pair check; FOLLOWUPS records the narrowing and notes that refactor(org): drop the charter tier from the interface #275 made
    the tier half of its second gap moot.

🤖 Generated with Claude Code

…ms change

`checkTakeover` admits a displacement only from a role the charter names in
Terms.Supervisors. Every lane in the live tenant names only the operator, so
the operator is the sole actor able to clear a seat held by a dead session —
which is exactly the page a supervisor lane exists to avoid.

Granting that authority is a terms change, and FOLLOWUPS explains why there is
no verb for one: a general recharter was written and withdrawn from #272
because `checkRecharter` verifies only min_reader monotonicity while
`checkWriter` accepts the holder's own incarnation, so a role could widen its
scope, lift its ceilings, add effect classes, or drop the supervisors that may
take it over — all self-signed. That is still true, and this does not change
it.

`submit` is admissible under that entry's own criteria rather than in spite of
them. It names supervisors as one of three attenuations a kernel can verify
("no shrink"), and adding one is monotone in the accountability direction: it
strictly grows the set of roles that may displace you and cannot widen your own
authority, so none of the five self-signed widenings are reachable. The guard
is total, not partial — every other term is carried through byte-identical,
including Tier, which is inert since #275 but still projected into every
digest.

Who writes it carries as much weight as what it says. Recharter is admitted
only from Held or Active under the writer's own incarnation, so a role submits
to oversight while sitting in its own seat. Consent, not imposition: nothing
can be made supervisable from outside, which is the property keeping this from
becoming the parent-authority mechanism the kernel still lacks.

Tests pin the property rather than the happy path: the whole of Terms survives
a submit byte-identical, takeover is refused with not_supervisor before and
admitted after, and re-submitting an existing supervisor writes no record.

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

itsHabib commented Sep 2, 2026

Copy link
Copy Markdown
Owner Author

@codex @claude Review this PR, pinned to 4133990d.

Sweep of stale open PRs: this one has been open without a complete review panel. Triggering the repository roster on the exact head above so it can be gated.

@claude

claude Bot commented Sep 2, 2026

Copy link
Copy Markdown

Claude finished @itsHabib's task in 2m 38s —— View job


Review of PR #276feat(org): submit — accept a supervisor

Reviewed at 4133990d.

Tasks

  • Load review tooling
  • Read changed files (main.go, main_test.go, FOLLOWUPS.md, CLAUDE.md, AGENTS.md)
  • Review implementation logic and guards
  • Review tests
  • Review docs/FOLLOWUPS updates
  • Post findings

Verdict

Looks good. One P2 (no-op path inconsistency under -json), documented below. No blocking issues.

The safety argument holds. The implementation is correct, the tests pin the right properties, and the FOLLOWUPS update is accurate.


Implementation (cmd/org/main.go:573–594)

The implementation is narrow and correct:

  1. Loads the current state (folds the chain — any unreadable chain errors here, not silently)
  2. Checks if the party is already a supervisor → no-op (idempotent)
  3. next := state.Terms copies the full struct, then slices.Clone(state.Terms.Supervisors) allocates a new backing array before appending — correct, no aliasing risk
  4. Calls appendAndReport with KindRecharter and the mutated terms

The byte-identity property is sound: because next is copied from the folded state, not reconstructed from CLI flags, every field that submit doesn't touch comes through exactly as the kernel last accepted it — including Tier (inert since #275 but still digested).

Kernel guards that apply at admission time:

  • checkRecharter (reduce.go:357): MinReader monotone; no negative min_reader — not changed by submit, passes trivially
  • Phase gate (reduce.go:145): KindRecharter is only admissible from Held or Active — the kernel enforces this, the CLI does not need to
  • checkWriter (reduce.go:244): If incarnation is provided and non-empty, it must match the current holder — the self-signed constraint

Findings

P2 — No -json support on the idempotent no-op path (main.go:587–590)

if slices.Contains(state.Terms.Supervisors, *party) {
    fmt.Fprintf(e.stderr, "%s already supervises %s; nothing to do\n", *party, s.role)
    return nil
}

When the supervisor is already present, exit 0 + empty stdout + stderr text. The normal write path correctly returns a JSON receipt when -json is set (via appendAndReport). A machine caller doing submit -party X -json can't tell "nothing written" from a missing output on success — it has to read stderr.

Contrast with cmdTransfer's reportNoOp(e, s, ...), which returns {"steps":[],"note":"..."} on stdout for -json callers. The same pattern would make submit consistent:

if slices.Contains(state.Terms.Supervisors, *party) {
    return reportNoOp(e, s, fmt.Sprintf("%s already supervises %s; nothing to do", *party, s.role))
}

This is P2 (not blocking for an operator-facing verb used interactively), but worth tracking since the JSON seam is load-bearing elsewhere in this codebase.

Informational — byte-identity invariant is CLI-enforced only

checkRecharter (kernel) only verifies MinReader is monotone. The guarantee that every other term survives identical is entirely in cmdSubmit. This is pre-existing, explicitly documented in FOLLOWUPS, and accepted. The test TestSubmitAddsSupervisorAndChangesNothingElse is the right guard: it asserts the whole Terms struct after a round-trip, not just the supervisor list.


Tests

The three new tests pin the right properties:

  • TestSubmitAddsSupervisorAndChangesNothingElse — asserts the full org.Terms struct after a boot -json round-trip, covering scope, effect classes, ceilings, and retire. A general recharter would zero those to flag defaults; this test would catch it. This is the test that makes the safety claim verifiable.
  • TestSubmitEnablesTakeover — the reason the verb exists: not_supervisor before submit, admission after. Uses org.ReasonNotSupervisor directly, so a kernel rename would break the test (good coupling).
  • TestSubmitIsIdempotentAndNeedsAParty — validates -party required, and that re-submitting an existing supervisor writes no record (checks org.KindRecharter absent from the log).

Coverage is complete for the stated properties.


Docs

FOLLOWUPS.md: The narrowing note is accurate and positioned correctly inside the existing entry. The reference to #275 making Tier inert is correct.

CLAUDE.md / AGENTS.md: Byte-identical (CI pair-check satisfied). The description correctly names the admission conditions and the consent-not-imposition property.

Usage string (main.go:121): Updated; submit is listed under lifecycle with an accurate one-liner.


Summary

The implementation correctly narrows KindRecharter to the one direction a kernel can verify (supervisor monotone), enforces byte-identity of all other terms via state-copy rather than flag reconstruction, and the tests pin both the property and the functional reason for the verb. The P2 above (no -json envelope on the no-op path) is a consistency gap against the transfer idiom — suggest a FOLLOWUPS entry or a quick fix before merge, but not a blocker.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T12:32:53.787251Z 4133990 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4133990d54

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmd/org/main.go Outdated
Comment thread cmd/org/main.go Outdated
…on retry

The new terms are derived from the loaded snapshot, so the append now
carries ExpectTip: two concurrent submits would otherwise each write
"old supervisors + mine" and the second to land would silently drop the
first's party. An idempotent retry under -json prints the same no-op
receipt shape transfer uses instead of an empty stdout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant