You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Let one person configure several Companies, chosen per repository - #30
A person who works for several organizations needs each to be its own Company: its own root key, facts, authorities and certificates. The launcher config admits one [company]. Merging organizations into one Company sends org A's rulings into org B's coding sessions. Swapping XDG_CONFIG_HOME per invocation doesn't work either, because the installed hooks run kinbase hooks dispatch <host> <event> with no arguments.
What
This PR adds a named form, [companies.<name>], which can't be combined with [company]. The single form is unchanged byte for byte, and the existing suite passes untouched.
The launcher picks at most one Company per invocation, from the repository the invocation acts on: --repo, the hook envelope cwd, or else the process cwd. From that Company it builds today's single-Company config, so build_trust, the caches, the projector and the hooks still see exactly one Company. The ratified "one externally configured Company root" therefore holds per repository and per invocation.
Selection only narrows. It looks at three kinds of evidence, from configured Companies only:
holds: the Company's cache keeps a certificate for the worktree UUID, and it verifies against that Company's root;
hint match: the Company's discovery_hints match the repository's normalized origin;
pin claim: the Company's cache already pins that origin.
The outcome:
One Company in the union is selected.
None means Codebase-only mode.
Two or more, or any evidence that can't be read, is a refusal: CONFIG_INVARIANT with detail.reason.
No kind of evidence outranks another. The UUID comes from worktree bytes and a certificate binds no origin, so a copied or forked .kin/ must not carry one organization's certificate into another organization's repository.
Also in this PR
Session candidates are bound to where they were minted. Under the named form, a Company candidate is company:<company_id> and records its repository_uuid. A checkpoint delivers it only from that repository with that Company selected; otherwise the candidate is held. Before this, candidates recorded only company:root and were admitted against the process cwd. A session that stopped in another organization's repository then delivered to that organization's Company. The probe for this fails when the guard is removed.
Hooks never block. When selection is refused or finds nothing, SessionStart gets a constant notice in the model context. On Claude it also gets a constant systemMessage that the person sees. Hook output never contains another Company's name, URL, cache path or company_id; a canary probe checks this.
Conversion from [company] is safe. If the per-Company default key would be minted next to the single form's existing key, the command refuses instead. To use one maintainer key for several Companies, set [identity] share_maintainer_key = true.
Revocation watermarks are keyed by company_id under the named form. A converted config carries its unqualified value over to one Company only.
Unselected repositories get their own local Company store, per repository, instead of one shared fallback.
project --evidence-repo refuses an evidence repository that resolves to a different Company.
status and doctor --repo show company_selection: the configured Companies, the evidence, the outcome and a remediation. This is terminal output only.
repo issue --company <name> takes a Company name under the named form. It refuses certified-elsewhere when another configured Company claims the repository, and the remediation gives the new-identity procedure: delete .kin/kinbase.toml, then run repo issue and repo init.
Spec
The error taxonomy is unchanged. spec/amendment-004-multiple-companies.md is a candidate that states the rule, adds the named form to cli.md, and lists the limitations and follow-ups:
certified-elsewhere is client-side only; server-side mint-only issuance is a follow-up.
Personal-store keys outlive a cache purge; company forget is a follow-up.
Cross-Company moves that keep signed history are a follow-up.
The amendment carries no authority until ratified. The code is meant to stand on today's ratified text.
How it was designed
The problem was put through a constrain interview (wandercom/constrain). The challenge phase settled what counts as "holding" a certificate and withdrew a proposed diagnostics ledger.
The resulting design was reviewed by the seven Advocate personas: Red Team, Adversarial, Sage, User, SME, Good Friend and Pat Helland. They found two defects in the first draft, and both are fixed here:
Cross-Company candidate delivery. Red Team, Adversarial and Helland each found it independently.
Worktree UUID outranking the origin. Red Team, Adversarial, SME and Helland flagged it.
They also prompted the key-migration guard, the refusal on unreadable evidence, the per-repository fallback store, the watermark carry-over and the human-visible notice.
Tests
crates/kinbase/tests/multiple_companies.rs runs two real company serve processes against an isolated HOME, XDG directories and Personal root:
each repository is served by its own Company, including from the hook envelope cwd;
a fork carrying another Company's UUID is refused, hooks exit 0 with no Company names or URLs, and the new-identity procedure recovers;
two Companies holding one UUID, forced by posting to the second server directly, is refused;
unreadable evidence refuses instead of falling through;
a repository no Company claims runs Codebase-only and the hook says so;
converting a config refuses until the legacy key is named;
sharing a maintainer key must be stated;
malformed named configs are refused;
a Company candidate minted in A's repository never reaches B's store.
There are also unit tests for hint normalization and matching (* and **) and for whole-token scrubbing.
cargo test: 267 passed, 0 failed (main: 245). Two probes were also checked against mutants of the code they guard: the candidate-binding guard removed, and "held certificate wins" restored. Both probes fail against those mutants.
Review round 1 (5662d0d): every finding from Copilot and Adapt was reproduced by a probe that fails on 3ea19f2 before being fixed. The exception is the repo init unreadable-root ordering, which was fixed by reading. Details are in each thread.
Also on this branch, found while setting up three real organizations:
175259a: repositories with no origin URL were all answered with the first such repository's certificate. Fixed in both the client and the service, and covered by a probe.
3ea19f2: the "no Company" notice now appears only in a repository that carries .kin/kinbase.toml, rather than in every directory a host opens.
Not covered by an end-to-end probe: the watermark carry-over (it needs a revocation fixture, so it has a unit test of the attribution instead) and project --evidence-repo across Companies.
A person who works for several organizations needs each to be its own
Company, but the launcher config admitted one [company]. Merging them sent
one organization's rulings into another's sessions; swapping
XDG_CONFIG_HOME per invocation was invisible to the installed hooks, which
run `kinbase hooks dispatch <host> <event>` with no arguments.
Add a named form, [companies.<name>], mutually exclusive with [company]
(which is unchanged). The launcher selects at most one Company per
invocation from the repository it acts on (--repo, the hook envelope cwd,
else the process cwd) and derives today's single-Company configuration
from it, so build_trust, caches, projector and hooks still see one
Company. Selection only narrows: the evidence is which configured
Companies hold a verifying certificate for the worktree's UUID, whose
discovery_hints match the normalized origin, and whose caches pin that
origin; exactly one is selected, none is Codebase-only, and several, or
any that cannot be read, is CONFIG_INVARIANT with a detail.reason. No
evidence outranks another, because the UUID is worktree bytes and a
certificate binds no origin: a copied .kin/ must not carry one
organization's certificate into another's repository.
Company candidates minted under the named form are bound to the selected
company_id and their repository UUID, and a checkpoint delivers them only
from that repository with that Company selected. Before this, candidates
recorded only company:root and were admitted against the process cwd, so
a session that stopped in another organization's repository delivered to
that organization's Company.
Also: revocation watermarks are keyed by company_id (carrying a converted
config's unqualified sighting to one Company), unselected repositories
get per-repository local Company stores, converting [company] refuses
rather than minting new keys, sharing one maintainer key needs
[identity] share_maintainer_key, hook output never names another Company
and tells the person (systemMessage) when no Company was chosen, and
status/doctor report the selection evidence.
The error taxonomy is unchanged. spec/amendment-004-multiple-companies.md
is a candidate stating the rule, the named form for cli.md, and the
limitations; it is not authority until ratified.
Spec: architecture.md "Trust and key lifecycle" (one Company root per repository and invocation); cli.md "Configuration"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoD6Wz3VRydW8LgHiWf3KL
The reason will be displayed to describe this comment to others. Learn more.
Confidence Score: 1/5
Summary
Adds repository-specific Company selection, but reproduced selection, redaction, checkpoint, and revocation defects make this unsafe to merge. Jeremy McEntire’s human review is required after fixes.
Important Files Changed
File
Overview
crates/kinbase/src/cli.rs
Repository-target routing and cross-Company projection checks.
crates/kinbase/src/config.rs
Named configuration, identity migration, and premature Company selection.
`repo issue` sent `discovery_hint: ""` for a repository with no origin
remote, and the service matched certificates by hint, so the second such
repository was answered with the first one's certificate: two
repositories shared one UUID and each other's facts. The client now omits
an empty hint and the service ignores one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoD6Wz3VRydW8LgHiWf3KL
With the hooks installed for every project, SessionStart told the person
"Kinbase found no Company" in every directory no configured Company
claims, home and scratch directories included, so the notice that marks a
real misconfiguration was buried by sessions that were never company work.
A refusal still always speaks; an empty selection now speaks only in a
repository that carries .kin/kinbase.toml, which was certified and lost
its Company.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoD6Wz3VRydW8LgHiWf3KL
Each defect below was reproduced by a probe that fails on 3ea19f2 and
passes here.
- A hook read the process cwd's Company before its envelope named the
repository: `cli` loaded the launcher for every command and `hooks run`
loaded it again, so a Company with missing credentials where the host
started failed SessionStart for a healthy one. Hook dispatch now parses
the envelope and sets its target before any launcher loads.
- Stop and PreCompact admitted candidates against the process cwd while
the Company was selected from the envelope cwd, holding candidates from
the very repository the host named. The checkpoint now takes the
envelope's repository.
- Hook output was scrubbed as text after encoding: identifiers survived in
the base64 envelope, replacement left the context frame's length stale,
and a Company named like a JSON literal (`true`) broke the document, so
the unscrubbed response went out instead. Scrubbing now walks JSON values
before anything is encoded or framed, and never falls back.
- Evidence lookups used `exists()`, which reads a permission error as
absence and let another Company's hint win; only NotFound is absence now,
and `repo init` reports unreadable roots before ambiguity.
- A refused `status` returned before attaching its selection report, the
case where the evidence is needed; the refusal now carries it.
- Two spellings of one cache root (`a/../cache`) passed the overlap check
before the directory existed; `.`/`..` are refused and paths compare by
their deepest existing ancestor.
- The single-form watermark went to whichever Company claimed it first,
racily and without regard to whose it was. It now carries over only to
the one Company whose verified snapshot carries that revocation, computed
rather than claimed; any doubt starts a fresh watermark.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RoD6Wz3VRydW8LgHiWf3KL
`company init` and `company serve` read only their own `--config`, but
`cli` loaded the launcher first, and the launcher derives the selected
Company's access from this user's config. Started inside a repository
whose Company had lost its facts token, `company init` for an unrelated
service failed with "facts token file is unreadable". The service
commands now dispatch before the launcher loads, as hooks do, and keep
its descriptor attestation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
Target selection pre-read the `--certificate` with `std::fs::read`, so a
caller-supplied file of any size was allocated whole before `repo init`'s
64 KiB ceiling could refuse it. It now goes through `read_bounded` with
the same ceiling, and an oversized file still falls back to the
repository target so `repo init` reports it.
`read_bounded` trusted the length from `symlink_metadata`, which is 0 for
a FIFO or a device and stale for a growing file, then read to end of
file. The read itself now stops one byte past the ceiling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
`store_snapshot` writes the snapshot row before its meta rows, so a crash
between them leaves a verified snapshot with no `company_id` row. Under
the named form `company_key_scope` then returned `None`, and
`trust_facts` read that as the single form: it read and wrote the
unqualified revocation watermark that every Company shares.
`cached_company_id` now falls back to the `company_id` in the stored
snapshot, which was signature-checked before it was stored and always
names its Company. When the named form still has no scope, `trust_facts`
reads and writes no watermark at all, so every warranted trace reopens
rather than borrowing another Company's cursor.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
A held candidate carries `destination: company:<company_id>`, and the
checkpoint puts it in the hook response. The scrubber learned other
Companies' ids only from their readable caches, so when that cache could
not be read (the `company-unverifiable` case, where hooks continue on
purpose) alpha's id reached beta's Stop output.
`Scrubber` now also rewrites the id of any `company:<id>` that is not
`company:root` and not the selected Company's own, from the shape of the
destination rather than the cache. It is built once per hook response
instead of reopening every Company's cache for each string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
company is a raw command-line string here, while configured Company names are validated identifiers. An argument containing controls or bidi/noncharacter code points reaches selection and is interpolated into the company-unknown error instead of being refused as an invalid identifier. Apply the same Company-name validator at the CLI boundary before constructing Target::Named.
Propagate refused evidence repository selection
crates/kinbase/src/cli.rs:467
A refused evidence-repository selection is collapsed to None here. When the primary repository is also unselected, an ambiguous or unreadable evidence repository therefore passes this equality check and its Codebase facts are mixed into the projection, bypassing the fail-closed selection rule. Propagate the evidence repository's refusal before comparing selected Companies.
Reserve context space for the required selection notice
crates/kinbase/src/hooks.rs:683
The selection notice is appended after all repository unknowns, but bound_evidence keeps unknowns in order and drops everything after the item/byte ceiling. A repository with enough existing unknowns can therefore lose the required refusal/no-Company notice from the model context. Put this constant notice first (or reserve space for it) so bounding cannot omit it.
Route Git subprocesses through World::isolate
crates/kinbase/tests/multiple_companies.rs:224
These Git subprocesses inherit the runner's live HOME and XDG configuration, so repository setup can read global Git config/templates instead of remaining inside the probe's temporary environment. Route the command through World::isolate just like the Kinbase subprocesses.
This issue also appears on line 829 of the same file.
Under several Companies, a repository no Company claims keeps its local
Company answers in a fallback store named by the hash of its canonical
path. Deleting a checkout and cloning another repository at the same path
reused the old store, so `corpus::load_store` read the previous
repository's signed local events and authority answers for the new one.
The namespace now also hashes the certified UUID, when there is one, and
the device and inode of the Git common directory, which a new clone
replaces.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
`same_company_evidence` compared the selected Company of each evidence
repository with this repository's, reading a refusal as "no Company". In
a repository no Company claims, an ambiguous or unverifiable evidence
repository compared equal and its Codebase facts went into the
projection. The evidence repository's refusal now stops the command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
Under several Companies `repo issue --company` names a configured
Company, and an unknown name went into the `company-unknown` message as
given, control and bidi characters included. A name that fails the same
validator configured names pass is now refused without being echoed.
The check lives in selection rather than clap because under the single
form `--company` is the service URL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
SessionStart appended the refusal or no-Company notice after the
repository's open unknowns, and `bound_evidence` keeps unknowns in order
and drops what follows the 32-item or byte ceiling. A repository with
enough unknowns lost the notice from the model's context. It now goes
first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
`World::repo` and the no-origin probe ran `git` with the runner's live
`HOME` and XDG directories, so a global Git config or template could
shape the repositories the probes built. They now go through
`World::isolate`, as the kinbase subprocesses do.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUe5FnuCbu7m6K3AsYoM2E
The four "Previously missed" findings from Copilot's review of a79f878 have no threads, so they're answered here. Each one reproduced, and each fix is pushed (head f4b389e).
Validate Company names at the CLI boundary (cli.rs:410)
Reproduced: repo issue --company $'evil\e[2J' printed the raw argument in the company-unknown message (probe fails on a79f878). Fixed in ba7772f. A name that fails config::valid_company_name, the same check configured names pass, is now refused without being echoed. The check sits in selection::resolve and not in clap, because under the single form --company is the service URL. Probe: a_company_name_from_the_command_line_is_validated_before_it_is_echoed.
Reproduced: from a repository no Company claims, project --evidence-repo <ambiguous fork> projected instead of refusing (probe fails on a79f878). Fixed in 1888cef. same_company_evidence returns the evidence repository's refusal before comparing selected Companies. Probe: a_refused_evidence_repository_is_refused_even_beside_an_unselected_one.
Reserve context space for the required selection notice (hooks.rs:683)
Reproduced: with 40 open unknowns, bound_evidence dropped the notice (unit test fails on a79f878). Fixed in 35aec6e. The notice now goes first among the unknowns. Test: hooks::tests::the_selection_notice_survives_the_projection_ceiling.
Route Git subprocesses through World::isolate (multiple_companies.rs:224, :829)
Fixed in f4b389e. Both git invocations now go through World::isolate. This is test hygiene, so there's no probe for it.
hooks.rs: the launcher now loads inside dispatch_event, after the
Company target is set, so the secret mask table is read after that
load. Hook output is masked for known secrets, then scrubbed of other
Companies' destinations.
The amendment-004 example named a real deployment; main's
generic_by_construction probe refused it, so it now uses globex.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UL8AgvtwuWA1D8sDqxTd1J
Brings in wandercom#31. `checkpoint_internal` now resolves its `repo` argument
once, above the admission loop, because wandercom#31 uses it for the batched
receipt sync and the checkpoint append. `company_binding_holds` moves
from `admit_candidate` into `admit_unsettled`: a settled receipt delivers
nothing, and every path that can deliver one goes through
`admit_unsettled`, the checkpoint loop included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DQZ8FhUz5avxQma8Bjyrrv
The reason will be displayed to describe this comment to others. Learn more.
Reproduced on e025856 with two local Company services and a repository whose origin matches Company A's hint. repo issue --company B returns issued, but installing that exact certificate with repo init exits 4 (company-ambiguous). This is one issuance/install failure mode: the command hands back a certificate that the normal next step rejects. Please make explicit issuance and installation agree about foreign hints, either by refusing issuance up front or by accepting the certificate under an intentional precedence rule.
The reason will be displayed to describe this comment to others. Learn more.
Reproduced separately on e025856 with two signed local Company services and 50 Personal observations. The old single-Company watermark for a key at revocation cursor 1001 is 42 and belongs to B. Both services have revoked that key at cursor 1001, but B's local signed snapshot is still the pre-revocation snapshot when A first reads it. A therefore migrates the legacy watermark to its own scoped key as 42. After B refreshes its signed snapshot, both Companies are recognized as revocation holders, but B's scoped watermark is created as 50 and A's incorrect 42 persists. Observations admitted before the original watermark can consequently be skipped when B evaluates the revocation. A cached snapshot that lacks a revocation cannot establish unique ownership of the legacy key; please make this carry-over conservative and ensure an early mistaken scoped write cannot remain authoritative.
A single-form watermark carried over to the one Company whose cached
snapshot carried that revocation, and the carry-over was written to the
Company's scoped key. A stale snapshot in the Company that really
recorded it made the wrong Company inherit the older cursor, for good,
so its observations admitted in between never reopened. Nothing on
this machine proves which Company recorded an unqualified watermark,
so none adopts it. Each starts fresh, which reopens more, never less.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012em5BvVC4KUcSKYMoDr4EK
`repo issue --company B` checked holders and pins but not hints, so it
issued B a certificate for a repository whose origin matched A's hint.
`repo init` counts that hint beside the certificate and refused it as
`company-ambiguous`. Issuance now refuses the same way, before handing
back a certificate the next step rejects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012em5BvVC4KUcSKYMoDr4EK
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A person who works for several organizations needs each to be its own Company: its own root key, facts, authorities and certificates. The launcher config admits one
[company]. Merging organizations into one Company sends org A's rulings into org B's coding sessions. SwappingXDG_CONFIG_HOMEper invocation doesn't work either, because the installed hooks runkinbase hooks dispatch <host> <event>with no arguments.What
This PR adds a named form,
[companies.<name>], which can't be combined with[company]. The single form is unchanged byte for byte, and the existing suite passes untouched.The launcher picks at most one Company per invocation, from the repository the invocation acts on:
--repo, the hook envelopecwd, or else the process cwd. From that Company it builds today's single-Company config, so build_trust, the caches, the projector and the hooks still see exactly one Company. The ratified "one externally configured Company root" therefore holds per repository and per invocation.Selection only narrows. It looks at three kinds of evidence, from configured Companies only:
discovery_hintsmatch the repository's normalized origin;The outcome:
CONFIG_INVARIANTwithdetail.reason.No kind of evidence outranks another. The UUID comes from worktree bytes and a certificate binds no origin, so a copied or forked
.kin/must not carry one organization's certificate into another organization's repository.Also in this PR
company:<company_id>and records itsrepository_uuid. A checkpoint delivers it only from that repository with that Company selected; otherwise the candidate is held. Before this, candidates recorded onlycompany:rootand were admitted against the process cwd. A session that stopped in another organization's repository then delivered to that organization's Company. The probe for this fails when the guard is removed.systemMessagethat the person sees. Hook output never contains another Company's name, URL, cache path or company_id; a canary probe checks this.[company]is safe. If the per-Company default key would be minted next to the single form's existing key, the command refuses instead. To use one maintainer key for several Companies, set[identity] share_maintainer_key = true.project --evidence-reporefuses an evidence repository that resolves to a different Company.statusanddoctor --reposhowcompany_selection: the configured Companies, the evidence, the outcome and a remediation. This is terminal output only.repo issue --company <name>takes a Company name under the named form. It refusescertified-elsewherewhen another configured Company claims the repository, and the remediation gives the new-identity procedure: delete.kin/kinbase.toml, then runrepo issueandrepo init.Spec
The error taxonomy is unchanged.
spec/amendment-004-multiple-companies.mdis a candidate that states the rule, adds the named form to cli.md, and lists the limitations and follow-ups:certified-elsewhereis client-side only; server-side mint-only issuance is a follow-up.company forgetis a follow-up.The amendment carries no authority until ratified. The code is meant to stand on today's ratified text.
How it was designed
The problem was put through a
constraininterview (wandercom/constrain). The challenge phase settled what counts as "holding" a certificate and withdrew a proposed diagnostics ledger.The resulting design was reviewed by the seven Advocate personas: Red Team, Adversarial, Sage, User, SME, Good Friend and Pat Helland. They found two defects in the first draft, and both are fixed here:
They also prompted the key-migration guard, the refusal on unreadable evidence, the per-repository fallback store, the watermark carry-over and the human-visible notice.
Tests
crates/kinbase/tests/multiple_companies.rsruns two realcompany serveprocesses against an isolated HOME, XDG directories and Personal root:cwd;There are also unit tests for hint normalization and matching (
*and**) and for whole-token scrubbing.cargo test: 267 passed, 0 failed (main: 245). Two probes were also checked against mutants of the code they guard: the candidate-binding guard removed, and "held certificate wins" restored. Both probes fail against those mutants.Review round 1 (5662d0d): every finding from Copilot and Adapt was reproduced by a probe that fails on 3ea19f2 before being fixed. The exception is the
repo initunreadable-root ordering, which was fixed by reading. Details are in each thread.Also on this branch, found while setting up three real organizations:
originURL were all answered with the first such repository's certificate. Fixed in both the client and the service, and covered by a probe..kin/kinbase.toml, rather than in every directory a host opens.Not covered by an end-to-end probe: the watermark carry-over (it needs a revocation fixture, so it has a unit test of the attribution instead) and
project --evidence-repoacross Companies.🤖 Generated with Claude Code
https://claude.ai/code/session_01RoD6Wz3VRydW8LgHiWf3KL