Skip to content

feat(org): platform seams — JSON receipts, presented identity, org-mcp, operator context.d - #263

Merged
itsHabib merged 1 commit into
mainfrom
feat/org-platform-seams
Aug 24, 2026
Merged

feat(org): platform seams — JSON receipts, presented identity, org-mcp, operator context.d#263
itsHabib merged 1 commit into
mainfrom
feat/org-platform-seams

Conversation

@itsHabib

Copy link
Copy Markdown
Owner

Summary

Stacked on #262. Turns the Baton home into a surface other tools and agents compose on — the platform-team slice: machine receipts, real identity presentation, an MCP surface, and operator-defined boot context.

What this adds

  • JSON receipts on every verb-json emits {kind, seq, digest, phase, tip, holder, active, dangling, held, fence}; status/verify/boot too. The JSON half of the exit-code seam.
  • Presented identity-incarnation/ORG_INCARNATION presents the id attach minted, so the kernel's stale_incarnation law bites; -strict/ORG_STRICT disables the single-operator write-as-holder default.
  • cmd/org-mcp — stdio MCP server exposing org verbs as native agent tools (org_boot, org_claim, org_yield, org_checkpoint, …). Shells the org binary (boundary law — same posture as console/escalate over gate); verb table IS the allowlist — charter/takeover/revoke/retire/delegate unreachable over MCP; kernel refusals surface as isError results carrying the reason id.
  • Operator context sources$ORG_STATE/<tenant>/<role>/context.d/* rides org boot (sorted, -context-bytes cap, truncation names the directory). The /continue-style "inject what I want the next session to see" mechanism, as plain files.

Validation

  • gofmt/go vet/golangci-lint clean; go test ./cmd/org/... ./cmd/org-mcp/... green (CLI translation, refusal→reason mapping, allowlist pinning, handshake framing, context ordering + truncation).
  • Live: real-protocol stdio smoke — org_status returns the board; org_claim on an unheld role returns {code:"refused", reason:"incarnation_missing"} for the agent to correct. Real boot for lead:agentic-development now carries an operator context.d note.

🤖 Generated with Claude Code

@itsHabib

Copy link
Copy Markdown
Owner Author

@claude please review.

Stacked on #262 (review that first). This adds the platform seams. Worth your attention:

  • cmd/org-mcp allowlist: charter/takeover/revoke/retire/recharter/delegate must be unreachable over MCP — the verb table is the only registry; check nothing else dispatches.
  • refusal mapping: org exit 1 → isError {code:refused, reason} with the reason regex extraction; exit ≥2 → {code:error}; runner exec failure must not masquerade as a refusal.
  • identity: -incarnation/ORG_INCARNATION presented on writes; -strict refuses write-as-holder for non-minting kinds only (charter/attach/takeover must stay exempt).
  • context.d: files ride org boot sorted, byte-capped, truncation names the directory; dotfiles and subdirs skipped; no traversal outside context.d.
  • JSON receipts: every write verb honors -json; receipt fields match the folded state after the append, not before.

@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown

Claude finished @itsHabib's task in 4m 46s —— View job


Review of PR #263 — platform seams (feat/org-platform-seams)

Reviewed at SHA decfc0e / a564dac (head of feat/org-platform-seams stacked on feat/org-home).

Todo

  • Read all changed files
  • Check allowlist: charter/takeover/revoke/retire/recharter/delegate unreachable over MCP
  • Check refusal mapping: exit 1 → isError refused+reason; exit ≥2 → error; exec failure ≠ refusal
  • Check identity: -incarnation/-strict semantics; exempt kinds
  • Check context.d: sorted, byte-capped, truncation label, dotfiles/subdirs skipped, no traversal
  • Check JSON receipts: all write verbs, fields from folded state after append
  • Post findings

Findings

P2 — TestAllowlistExcludesStructureVerbs missing org_recharter

server_test.go:111 pins five banned names but omits "org_recharter":

for _, banned := range []string{"org_charter", "org_takeover", "org_revoke", "org_retire", "org_delegate"} {

Both server.go:13 and the stated invariant in the trigger comment explicitly include recharter as an excluded verb. The test's lookupVerb check at line 116 only asserts against org_charter. This is the only registry — lookupVerb is the right place to assert — but the pinned set is incomplete.

recharter doesn't exist as a CLI verb today so it literally can't be invoked, but the test is supposed to pin the stated surface contract. If recharter is ever added to the CLI the allowlist test would silently miss it.

Fix this →

P2 — cmdAttach receipt omits Active and Dangling

cmd/org/main.go:297–300 constructs the attach receipt inline and drops two fields that every other write verb includes:

return printJSON(e, receipt{
    Kind: r.Kind, Seq: r.Seq, Digest: digest, Phase: state.Phase,
    Tip: state.Tip, Holder: state.Holder, Held: len(state.Held), Fence: state.Fence,
    // missing: Active, Dangling
})

appendAndReport (line 228) populates both. After a prior incarnation there can be a dangling claim from a predecessor — exactly the situation the agent must know about to act correctly. Omitting these fields means a machine caller reading the attach receipt can't detect the dangling obligation it needs to resolve. Since they're omitempty the fix is a two-field addition.

Fix this →


P3 — refusalReason regex requires at seq N suffix

server.go:196:

var refusalPattern = regexp.MustCompile(`\b([a-z_]+) at seq \d+`)

If the kernel ever emits a refusal that doesn't follow the "<reason> at seq N" format, refusalReason returns "" and only detail carries the stderr. The agent then sees {code:"refused", reason:""} with no machine-readable reason to branch on. The detail field still has the full stderr so it's not a silent failure, but the whole point of the reason extraction is agent-branchability. Worth noting if the kernel's refusal message format can vary (e.g. an admission-time check that has no seq position yet).

P3 — No test coverage for -strict / -incarnation identity policy

main_test.go's TestVerbLoopExitCodes doesn't exercise:

  • -strict refusing a write when ORG_INCARNATION is absent
  • -incarnation being propagated correctly to the Draft
  • The minting-kind exemption (charter, attach, takeover must pass even in strict mode)

This is the security invariant the trigger comment calls out. A table-driven test over these three cases (strict+non-minting kind → error; strict+minting kind → allowed; no-strict → allowed regardless) would close the gap. Tests in server_test.go cover the MCP translation layer but nothing tests that the strict policy applies correctly end-to-end.

P3 — minted() / mints() duplication

cmd/org/main.go:238 and cmd/org/internal/home/home.go:199 are byte-for-byte the same function under different names. Both must agree on which kinds are identity-minting. If KindTakeover logic changes they'd need to be updated in two places. This is a consequence of the boundary law (main can't import home's unexported symbol), but worth flagging as a sync risk.


Checklist (against the trigger invariants)

Invariant Verdict
Allowlist: structure verbs unreachable via verb table ✅ — verbs slice is the only dispatch path; lookupVerb + dispatch are the only routes; "tools/call" resolves through lookupVerb and nothing else
recharter pinned in allowlist test ❌ — missing from banned list in TestAllowlistExcludesStructureVerbs
Refusal mapping: exit 1 → {code:"refused", reason} ✅ — invoke() checks err != nil before the switch, so exec failure never enters exit-1 branch
exec failure → {code:"exec_failed"} ✅ — Shell returns (nil, nil, 0, err) for non-exit errors; invoke gates on err != nil first
exit ≥2 → {code:"error"} ✅ — default branch
Identity: -incarnation/ORG_INCARNATION propagated ✅ — appendAndReport sets d.Incarnation = s.incarnation when empty
-strict blocks non-minting writes without incarnation ✅ — correct logic in appendAndReport
Minting kinds (charter/attach/takeover) exempt from strict ✅ — minted() covers all three; cmdAttach bypasses appendAndReport entirely
context.d: sorted by name ✅ — os.ReadDir returns sorted entries
context.d: byte-capped on text path ✅ — contextText enforces -context-bytes
context.d: truncation names the directory ✅ — dir arg from h.ContextDir(...)
context.d: dotfiles skipped ✅ — strings.HasPrefix(e.Name(), ".")
context.d: subdirs skipped ✅ — e.IsDir()
context.d: no traversal ✅ — os.ReadDir returns filenames only; no path separator possible (symlinks not filtered, but operator-controlled dir)
JSON receipts on all write verbs ✅ — all routes via appendAndReport; cmdAttach has its own inline path
Receipt fields from folded state after append ✅ — h.Append returns next (result of org.Advance); all receipt construction uses this next
cmdAttach receipt field completeness ❌ — Active/Dangling absent

Overall

The core architecture is clean: verb table = allowlist is the right invariant and it's airtight, the exit-code tripartite split correctly separates exec failure from refusal, and the context.d mechanism is deliberately minimal. Two P2s (test gap and attach receipt fields) that should be fixed before merging; the rest are informational.

itsHabib added a commit that referenced this pull request Aug 24, 2026
The brief's §6 note said dossier task_list omits notes and no CLI verb
reads one. Re-verified against the installed binary and source: refuted —
task_list returns structured notes, so the write tier can read
conclusions back. The correction names the two arguments that leaned on
the false premise (hooks #43's substrate-coupling rationale,
store-decision finding 2) rather than silently rewriting them.

where-this-stands gains a build update: the re-entry slice is no longer
a claim — cmd/org and cmd/org-mcp shipped CI-green in PRs #262/#263,
with the deliberate deviations recorded where they were made.

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

Copy link
Copy Markdown
Owner Author

@claude re-review on the current head.

Round-1 fixes: both P2s (org_recharter added to the banned-list pin; attach receipt now carries Active/Dangling — matters after a revoke orphans a claim) and all three P3s (refusalReason prefix fallback; -strict/-incarnation test covering exempt minting kinds + pre-append strict refusal + stale_incarnation on a wrong id; minted/mints deduped into exported home.MintsIdentity). Also merged in the base branch's round-1 fixes (hook fail-open, cmdBlob message).

@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown

Claude finished @itsHabib's task in 3m 51s —— View job


Re-review of PR #263 — platform seams (round-1 fixes verified)

Reviewed at SHA dc71783 (round-1 fix commit) stacked on 8e72b0d (base-branch merge).

Todo

  • Read current HEAD diff vs base branch
  • Verify P2 fix: org_recharter in banned-list test
  • Verify P2 fix: attach receipt carries Active/Dangling
  • Verify P3 fix: refusalReason prefix fallback
  • Verify P3 fix: -strict/-incarnation test coverage
  • Verify P3 fix: minted/mints deduped into exported home.MintsIdentity
  • Check base-branch merges (hook fail-open, cmdBlob message)
  • Scan for new issues introduced by the fixes
  • Post final review

Round-1 fix verification

All five findings addressed correctly. Detailed checklist:

P2: org_recharter in banned-list test ✅

server_test.go:111 now pins all six structure verbs:

for _, banned := range []string{"org_charter", "org_takeover", "org_revoke", "org_retire", "org_recharter", "org_delegate"} {

Matches the stated surface contract in server.go:13 and org-mcp/CLAUDE.md. The lookupVerb assertion at line 116 still holds.

P2: Attach receipt Active/Dangling

main.go:290–295 — the fix is in the right place and complete:

return printJSON(e, receipt{
    Kind: r.Kind, Seq: r.Seq, Digest: digest, Phase: state.Phase,
    Tip: state.Tip, Holder: state.Holder, Active: state.Active,
    Dangling: state.Dangling, Held: len(state.Held), Fence: state.Fence,
})

Field set is now byte-identical to appendAndReport's receipt path.

P3: refusalReason prefix fallback ✅

server.go:198–210 — two-pattern fallback is correct:

var (
    refusalPattern = regexp.MustCompile(`\b([a-z_]+) at seq \d+`)
    refusalPrefix  = regexp.MustCompile(`^org: ([a-z_]+):`)
)

Primary pattern (with seq clause) tried first; prefix fallback only on miss. Exit-4 paths never reach this since run() in main.go routes non-refusal errors to codeError, not codeRefused. The ^ anchor on the fallback is correct — kernel stderr starts with "org: ".

P3: -strict/-incarnation identity test ✅

TestStrictIdentityPolicy (main_test.go:109–133) covers the three required cases:

  1. charter -strict → exit 0: minting kind bypasses appendAndReport's strict check (home.MintsIdentity returns true). ✅
  2. attach -strict → exit 0: cmdAttach bypasses appendAndReport entirely and calls h.Append directly — -strict is parsed but not checked. ✅
  3. assign -strict (no incarnation) → codeError + "strict mode": the pre-append guard in appendAndReport fires. ✅
  4. assign -incarnation sha256:000... (stale, no -strict) → codeRefused + "stale_incarnation": the kernel's law fires independently of the strict policy. ✅

The takeover minting-kind exemption is not exercised directly (it requires a chain in held state — complex to set up in isolation), but it's covered by MintsIdentity's own return value and the fact that cmdParty calls appendAndReport which calls MintsIdentity. The logical coverage is sound even without a direct test path. Informational only.

P3: home.MintsIdentity deduplication ✅

home.go:199–205 — single source of truth, exported, with a comment explaining why:

// Exported because the CLI's identity policy branches on the same fact and a
// second copy of this list is how the two would drift.
func MintsIdentity(kind string) bool {
    return kind == org.KindCharter || kind == org.KindAttach || kind == org.KindTakeover
}

main.go local minted() function is gone. Both home.go:176 and main.go:210 now call home.MintsIdentity.


Base-branch merge verification

Hook fail-open (sessionstart-boot.sh, stop-mark.sh) ✅

All jq calls now carry 2>/dev/null and either || exit 0 or || true:

cwd="$(jq -r '.cwd // empty' <<<"$input" 2>/dev/null)" || exit 0
# stop_hook_active uses && so jq failure falls through silently
[ "$(jq -r '.stop_hook_active // false' <<<"$input" 2>/dev/null)" = "true" ] && exit 0

Empty or non-JSON stdin exits 0. Fail-open contract delivered.

cmdBlob not-found message

main.go:579: "blob %s is erased or unknown", s.fs.Arg(0) — names the parsed digest, not the raw arg.


Invariant checklist (updated)

Invariant Round 1 Round 2
Allowlist: structure verbs unreachable via verb table
recharter pinned in allowlist test ✅ fixed
Refusal mapping: exit 1 → {code:"refused", reason}
exec failure → {code:"exec_failed"}, never refusal
exit ≥2 → {code:"error"}
refusalReason: prefix fallback for seq-less messages ✅ fixed
Identity: -incarnation propagated; strict pre-append gate
-strict test covering minting exemptions + stale id ✅ fixed
Minting kind list: single definition, no drift risk ✅ fixed
cmdAttach receipt: Active/Dangling included ✅ fixed
context.d: sorted, byte-capped, truncation named, no traversal
Hook fail-open on malformed stdin ❌ (base) ✅ fixed

Overall

All P2s and P3s from round 1 are correctly addressed. The attach receipt, allowlist test, refusal fallback, identity policy test, and MintsIdentity deduplication are all clean. The base-branch merge picks up hook fail-open and cmdBlob correctly. No new issues introduced.

Ready to merge.

@itsHabib
itsHabib changed the base branch from feat/org-home to main August 24, 2026 18:28
…p, operator context

Four seams that turn the home from a demo into a surface other things
compose on:

Receipts: every write verb takes -json and emits the machine half of the
exit-code seam — kind, seq, digest, phase, tip, holder, active, dangling,
held, fence. status/verify/boot speak JSON too.

Identity: -incarnation (ORG_INCARNATION) presents the id attach minted,
letting the kernel's stale_incarnation law actually bite; -strict
(ORG_STRICT) refuses the single-operator write-as-holder default. The
minting-kind list lives once, exported as home.MintsIdentity.

org-mcp: a stdio MCP server exposing the org verbs as native agent
tools. It shells the org binary and composes on its exit codes — never
imports the home — and its verb table is the allowlist: charter,
takeover, revoke, retire, recharter and delegate have no entry, so the
org's structure cannot be reshaped over MCP. A kernel refusal surfaces
as an isError result carrying the reason id for the agent to branch on.

Operator context: files dropped in <role>/context.d/ ride the boot
output, sorted, byte-capped, truncating with a pointer to the directory.
The dumbest mechanism that works — writing a file is publishing,
deleting it is revocation.

Includes review round-1 fixes: org_recharter pinned in the allowlist
test, attach receipts carrying Active/Dangling, a refusalReason prefix
fallback, and a test for the -strict/-incarnation identity policy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@itsHabib
itsHabib force-pushed the feat/org-platform-seams branch from dc71783 to b0fa010 Compare August 24, 2026 18:29
@itsHabib

Copy link
Copy Markdown
Owner Author

@claude review this PR and publish your findings as a FORMAL GitHub review pinned to head b0fa010 — not an issue comment.

Run exactly: gh pr review 263 --repo itsHabib/workbench --comment --body "<your review, stating the head SHA>"

Context: the branch was rebuilt onto main as a single commit after #262 squash-merged (same content you already reviewed clean in round 2, plus the round-1 fixes). Re-verify the platform seams: org-mcp allowlist excludes every structure verb, refusal mapping (exit 1 → refused+reason, exec failure ≠ refusal), -strict/-incarnation identity policy with minting kinds exempt, context.d ordering/cap/traversal, and JSON receipts reflecting post-append state.

gate cannot count an issue comment: classifyPanel requires User.Type=Bot, a COMMENTED/APPROVED/CHANGES_REQUESTED state, and commit_id == the judged head. If the review command fails, say so plainly.

@claude

claude Bot commented Aug 24, 2026

Copy link
Copy Markdown

Claude finished @itsHabib's task in 6m 3s —— View job


Re-review of PR #263 — platform seams (rebuilt onto main)

Todo

  • Load tools
  • Read changed files: server.go, server_test.go, verbs.go, main.go, home.go, main_test.go
  • Verify allowlist: structure verbs unreachable via verb table (including recharter)
  • Verify refusal mapping: exit 1 → refused+reason; exec failure ≠ refusal; exit ≥2 → error
  • Verify identity: -strict/-incarnation propagation; minting kinds exempt
  • Verify context.d: sorted, byte-capped, truncation named, no traversal
  • Verify JSON receipts: all write verbs; post-append state
  • Post formal GitHub review via gh pr review 263 --repo itsHabib/workbench --comment

Formal review posted pinned to head b0fa010329f60690c6cb5616b5f296b8d99fa73f. Full findings below.


Allowlist — structure verbs unreachable over MCP ✅

verbs.go:127 — the verbs slice is the only dispatch registry. The sole resolution path is handleMessage → dispatch → handleToolsCall → lookupVerb → v.args → invoke. lookupVerb does a linear scan of this slice and nothing else; there is no second dispatch table or special-case route. The banned six have no entry: org_charter, org_takeover, org_revoke, org_retire, org_recharter, org_delegate.

server_test.go:111 pins all six banned names including org_recharter, plus asserts lookupVerb("org_charter") returns false. Pin is complete and matches server.go:13 and cmd/org-mcp/CLAUDE.md.

Refusal mapping — tripartite exit-code split ✅

Shell (server.go:41–56) returns (nil, nil, 0, err) for non-ExitError failures and (stdout, stderr, code, nil) for all exit-code cases — the error and code channels are mutually exclusive. invoke (server.go:176) gates on err != nil before the switch, so exec failure is always classified "exec_failed" and never enters the exit-1 branch. Exit 1 → {code:"refused", reason, detail}. Exit ≥2 → {code:"error", exit, detail}.

refusalReason (server.go:203–211) — two-pattern fallback: primary matches <reason> at seq N; fallback matches ^org: <reason>: for seq-less messages. The ^ anchor is correct — kernel stderr opens with "org: ". A message matching neither degrades to reason:"" with full stderr in detail.

Identity — -strict/-incarnation, minting kinds exempt ✅

newScope (main.go:138–149) wires -incarnation defaulting to ORG_INCARNATION and -strict defaulting to ORG_STRICT != "".

appendAndReport (main.go:209–234): pre-append strict guard (s.strict && s.incarnation == "" && !home.MintsIdentity(d.Kind)) returns codeError before any append. Incarnation propagated via d.Incarnation = s.incarnation.

home.MintsIdentity (home.go:204) — single exported definition: KindCharter || KindAttach || KindTakeover. No local copy in main.go; no drift risk. Used by both appendAndReport (strict guard) and home.draft (incarnation stamping).

cmdAttach bypasses appendAndReport entirely (calls h.Append directly at main.go:282) — correct, since attach IS the identity-minting event.

TestStrictIdentityPolicy (main_test.go:112–133) covers four cases: charter -strict → exit 0 (minting kind exempt); attach -strict → exit 0 (cmdAttach bypasses guard); assign -strict (no incarnation) → codeError + "strict mode"; assign -incarnation sha256:000… (stale) → codeRefused + "stale_incarnation".

context.d — ordering, byte cap, truncation label, no traversal ✅

home.Context (home.go:248–269): os.ReadDir returns sorted entries; e.IsDir() skips subdirs; strings.HasPrefix(e.Name(), ".") skips dotfiles; os.ReadFile(filepath.Join(dir, e.Name())) — filenames from ReadDir contain no path separator, traversal outside context.d is structurally impossible.

contextText (main.go:479–498): byte cap enforced per entry before writing; truncation message "… context truncated at %d bytes — read the rest in %s\n" names the directory from h.ContextDir.

TestBootInjectsOperatorContext (main_test.go:80–107) covers sorted ordering, presence, and truncation at 40 bytes.

JSON receipts — all write verbs, post-append state ✅

home.Append (home.go:123–149) returns (r, next, nil) where next = org.Advance(state, r) — the post-append state. All receipt construction uses this return value: appendAndReport (main.go:228) and cmdAttach's inline path (main.go:291–295).

cmdAttach receipt carries all 10 fields: Kind, Seq, Digest, Phase, Tip, Holder, Active, Dangling, Held, Fence — byte-identical to appendAndReport's path.


Invariant checklist

Invariant Result
Allowlist: structure verbs absent from verb table
org_recharter pinned in banned-list test
exit 1 → {code:"refused", reason, detail}
exec failure → "exec_failed", never enters exit-code switch
exit ≥2 → {code:"error"}
refusalReason: seq-clause primary + prefix fallback
-incarnation/ORG_INCARNATION propagated to draft
-strict pre-append guard; non-minting kinds only
Minting kinds (charter/attach/takeover) exempt
home.MintsIdentity: single exported definition
-strict/-incarnation test: 4 cases covered
context.d: sorted, byte-capped, truncation names directory
context.d: dotfiles/subdirs skipped, no traversal
JSON receipts on all write verbs
Receipt fields from post-append state
cmdAttach receipt: all 10 fields including Active/Dangling

All invariants verified clean against head b0fa010329f60690c6cb5616b5f296b8d99fa73f. No findings. Ready to merge.

@claude claude 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.

Reviewed at SHA b0fa010 (single squash commit, rebuilt onto main after #262 merged).

Re-verification of all platform seams against the stated invariants.


Allowlist — structure verbs unreachable over MCP ✅

verbs.go:127 — the verbs slice is the only dispatch registry. The sole resolution path is handleMessage → dispatch → handleToolsCall → lookupVerb → v.args → invoke. lookupVerb does a linear scan of this slice and nothing else; there is no second dispatch table or special-case route. The banned six have no entry: org_charter, org_takeover, org_revoke, org_retire, org_recharter, org_delegate — all absent from the slice. The tools/call handler cannot reach them.

server_test.go:111 pins all six banned names, including org_recharter, plus asserts lookupVerb("org_charter") returns false. The pin is complete and matches the stated surface contract in server.go:13 and cmd/org-mcp/CLAUDE.md.

Refusal mapping — tripartite exit-code split ✅

server.go:176–192. The Shell runner (server.go:41–56) returns (nil, nil, 0, err) for non-ExitError failures and (stdout, stderr, code, nil) for all exit-code cases — the error channel and the code channel are mutually exclusive. invoke checks err != nil first, before entering the switch, so exec failure is classified "exec_failed" and never enters the exit-1 branch. Exit 1 → {code:"refused", reason, detail}. Exit ≥2 → {code:"error", exit, detail}. The classification is airtight.

TestRefusalSurfacesReason (server_test.go:61) drives exit-1 through the full serve loop and asserts isError=true, code="refused", reason="dangling_claim". TestMissingArgumentIsToolError (server_test.go:84) confirms a pre-flight validation error never reaches the process boundary.

refusalReason (server.go:203–211) — two-pattern fallback: primary matches <reason> at seq N; fallback matches ^org: <reason>: for seq-less messages. The ^ anchor on the fallback is correct — kernel stderr opens with "org: ". A message matching neither degrades to reason:"" with full stderr in detail, which is still machine-inspectable.

Identity — -strict/-incarnation, minting kinds exempt ✅

newScope (main.go:138–149) wires -incarnation defaulting to ORG_INCARNATION and -strict defaulting to ORG_STRICT != "".

appendAndReport (main.go:209–234):

  1. Pre-append strict guard: if s.strict && s.incarnation == "" && !home.MintsIdentity(d.Kind) → returns codeError with "strict mode" before any append.
  2. Incarnation propagation: d.Incarnation = s.incarnation when the draft carries none — the kernel's stale_incarnation law bites on a wrong presented id.

home.MintsIdentity (home.go:204) — single exported definition: KindCharter || KindAttach || KindTakeover. Used by appendAndReport for the strict guard and by home.draft for the incarnation-stamping decision. No local copy in main.go; no drift risk.

cmdAttach bypasses appendAndReport entirely (calls h.Append directly at main.go:282) — correct because attach IS the identity-minting event; the strict check does not apply by definition.

TestStrictIdentityPolicy (main_test.go:112–133) covers four cases:

  • charter -strict → exit 0: minting kind, exemption fires.
  • attach -strict → exit 0: minting kind, cmdAttach bypasses the guard.
  • assign -strict (no incarnation) → codeError + "strict mode": pre-append guard fires.
  • assign -incarnation sha256:000… (stale, no strict) → codeRefused + "stale_incarnation": kernel law fires independently.

takeover minting-kind exemption is not tested end-to-end (requires a held-state chain), but is covered by MintsIdentity's own return value and appendAndReport's call to it — the gap is informational.

context.d — ordering, byte cap, truncation label, no traversal ✅

home.Context (home.go:248–269):

  • os.ReadDir returns entries sorted by name — sorted ordering requirement met.
  • e.IsDir() guard skips subdirectories.
  • strings.HasPrefix(e.Name(), ".") skips dotfiles.
  • os.ReadFile(filepath.Join(dir, e.Name())) — filenames from ReadDir contain no path separator, so traversal outside context.d is structurally impossible (symlinks not filtered, but the directory is operator-controlled).

contextText (main.go:479–498):

  • if spent+len(entry) > budget enforces the byte cap before writing each entry.
  • Truncation message: "… context truncated at %d bytes — read the rest in %s\n" names the directory (dir arg from h.ContextDir).

TestBootInjectsOperatorContext (main_test.go:80–107) exercises sorted ordering, presence, and truncation with a 40-byte cap — all three branches confirmed by assertion.

JSON receipts — all write verbs, post-append state ✅

home.Append (home.go:123–149) returns (r, next, nil) where next is org.Advance(state, r) — the state after the admitted record.

appendAndReport (main.go:209–234) constructs the receipt from r (the appended record) and state (the post-advance return value). The Phase, Tip, Holder, Active, Dangling, Held, Fence fields reflect the folded state after the append, not before.

cmdAttach (main.go:291–295) constructs its receipt from the same h.Append return values with all ten fields — Kind, Seq, Digest, Phase, Tip, Holder, Active, Dangling, Held, Fence — byte-identical to appendAndReport's path.

All write verbs route through either appendAndReport or cmdAttach's inline path; both honor -json. Read verbs (status, verify, boot) construct their own JSON shapes correctly.


Invariant checklist

Invariant Result
Allowlist: structure verbs absent from verb table
org_recharter pinned in banned-list test
Refusal: exit 1 → {code:"refused", reason, detail}
exec failure → {code:"exec_failed"}, never enters exit-code switch
exit ≥2 → {code:"error"}
refusalReason: seq-clause primary + prefix fallback
-incarnation/ORG_INCARNATION propagated to draft
-strict pre-append guard; non-minting kinds only
Minting kinds (charter/attach/takeover) exempt from strict
home.MintsIdentity: single exported definition, no drift
-strict/-incarnation test: 4 cases covered
context.d: sorted by name
context.d: byte-capped before each entry
context.d: truncation names the directory
context.d: dotfiles and subdirs skipped
context.d: no traversal outside the directory
JSON receipts on all write verbs (-json honored)
Receipt fields from folded state after append
cmdAttach receipt: all 10 fields including Active/Dangling

All invariants verified clean against head b0fa010. No findings.

🤖 Generated with Claude Code

@claude claude 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.

test

@claude claude 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.

Reviewed at SHA b0fa010. All platform seams verified clean — no findings. Full review in the PR comment thread.

@itsHabib
itsHabib merged commit 12f416d into main Aug 24, 2026
9 checks passed
itsHabib added a commit that referenced this pull request Aug 25, 2026
…collide

cmd/org and cmd/org-mcp are on main (#262, #263). The re-entry surface
the reconciliation brief called the closest-to-buildable gap is built:
charter/attach/assign/claim/yield/checkpoint/boot plus sessionstart-boot
and stop-mark hook scripts. It is not wired — settings.json still has
only PreToolUse and PostToolUse.

That makes the brief's §5 stale and surfaces something sharper. There are
now TWO Stop hooks, neither wired, built days apart by different sessions
with no reference to each other: cmd/org/hooks/stop-mark.sh appends a
mechanical mark to the role's chain, and hooks PR #42's stop-discharge.sh
appends what a session did to the dossier tasks it touched. Both refuse
to ask the model, and their headers reach that conclusion independently
in different words — which is the strongest signal in this corpus and
also a duplication that will double-write if both are installed.

The handoff leads with that as the deliverable rather than the vocabulary
question. It also carries the two corrections this session earned: the
notes error (a skip_serializing_if field sampled at .[0] reads exactly
like a field that does not exist) and the mktemp BSD/GNU divergence that
passed every local run and failed every CI one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
itsHabib added a commit that referenced this pull request Aug 26, 2026
…cisions to fold

Independent reconciliation of the whole ownership-continuity lineage at
2026-08-26 heads: what #245 designed, what #246/#248/#262/#263 shipped
through the governed path, what #265/#266 measure, what hooks#42/#43 and
drive#47 add, what cc-skills#29 proves in fixture, and what remains
hypothesis. Every major claim classified on the honest rung ladder;
duplications and missing joins named; D1-D10 written to be accepted,
amended, or struck in place. Indexed from vision.md's document table.

Co-Authored-By: Claude Fable 5 <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