Compare commits
1 Commits
main
...
feat/834-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3831d5e377 |
@@ -40,4 +40,20 @@ This is the named D2-v5 T-A residual. It is distinct from an observer that fires
|
||||
| Generation advances on reload/new/resume/fork | Prior incarnation revoked; replacement starts `UNVERIFIED`. |
|
||||
| Lifecycle observers and all-tools gate both fail or are removed | T-C total-hook-miss residual; protected-branch controls remain required. |
|
||||
|
||||
## T-C server-side branch-protection posture
|
||||
|
||||
The required posture is that `main` is push-blocked and PR-only-merge is **MANDATORY**, regardless
|
||||
of client-gate state. The client-side gate narrows the exposure window only; it is not the T-C
|
||||
guarantee. The server-side protected-branch configuration is the irreducible guarantee for protected
|
||||
repository actions. Status-check enforcement and approval enforcement are **RECOMMENDED**.
|
||||
|
||||
## Current-vs-required gap (recorded, not enacted)
|
||||
|
||||
The current empirical configuration is recorded here without re-probing or mutating live branch
|
||||
protection. `enable_push=False` (push-block present), so the mandatory push-block/PR-only-merge core
|
||||
holds. `require_approvals=0` (approvals not enforced), `enable_status_check=False` (status checks not
|
||||
enforced), and `block_on_official_review=False` (official review not enforced). Those recommended
|
||||
merge-quality controls are the current gap; changing them is a separate, owner-gated operations
|
||||
decision and is not enacted by this documentation change.
|
||||
|
||||
The permanent T12b/T30 acceptance case prints both required outcomes: dual-hook miss within TTL is **ALLOWED**, and the same lease after TTL is **DENIED**. Separate real-socket tests prove each Claude observer and same-PID generation rollover; Pi lifecycle tests exercise pre/post observers, all four replacement reasons, and local failure closure.
|
||||
|
||||
@@ -24,6 +24,12 @@ The daemon owns a second protected production observer socket (mode `0600`) unle
|
||||
|
||||
A higher generation for the same anchor atomically replaces the stored incarnation and deletes all prior tokens and lease authority for that session. A lower generation is stale. Runtime descendants resolve the current generation from an owner-only, locked generation file created by the register-before-exec launcher; reload/new/resume/fork observers advance and `fsync` it before broker revocation. This supports generation replacement even when PID/starttime do not change. Tokens are 256-bit values from the operating-system cryptographic RNG and are single use. At most 256 pending tokens may be persisted; another mint fails with `TOKEN_CAPACITY` before mutation. Successful consumption deletes the token, while a replay still fails with `TOKEN_REPLAY`. Live v1 token records retain the existing `consumed: false` schema.
|
||||
|
||||
VERIFIED leases are volatile and monotonic-time bounded: broker restart, generation change, explicit observer revocation, or expiry returns the session to `UNVERIFIED`. `begin_verification` always revokes before minting a new prerequisite. `begin_recovery` reuses that exact transition and mints a new challenge, so a normal-path receipt/challenge cannot be replayed through recovery. `promote_lease` is valid only from the matching pending cycle; persistence failure rolls token and lease state back, while post-rename durability uncertainty terminates the broker. The WI-1 token is the atomic promotion prerequisite substrate. Receipt evidence is a T-A delivery/liveness prerequisite only; it cannot replace the mechanical mutator gate as safety authority. A tail-preserving middle drop is not receipt-detectable and remains the disclosed T-C/server-side residual.
|
||||
VERIFIED leases are volatile and monotonic-time bounded: broker restart, generation change, explicit observer revocation, or expiry returns the session to `UNVERIFIED`. `begin_verification` always revokes before minting a new prerequisite. `begin_recovery` reuses that exact transition and mints a new challenge, so a normal-path receipt/challenge cannot be replayed through recovery. `promote_lease` is valid only from the matching pending cycle; persistence failure rolls token and lease state back, while post-rename durability uncertainty terminates the broker. The WI-1 token is the atomic promotion prerequisite substrate.
|
||||
|
||||
## Receipt boundary and T-C residual (R1)
|
||||
|
||||
Receipt evidence is a T-A delivery/liveness prerequisite only; it cannot replace the mechanical
|
||||
mutator gate as safety authority. The receipt detects an **ABSENT** or **PREFIX-TRUNCATED** terminal
|
||||
token. A **MIDDLE-DROP** that preserves the tail is a T-C contract violation that is **NOT receipt-detectable**. It is covered by server-side protected-branch controls, **NOT** by the receipt; no category-wide receipt-detection claim is made for that tail-preserving transformation.
|
||||
|
||||
State replacement serializes and enforces the 4 MiB maximum before opening a temporary file, then uses a mode-`0600` temporary file, `fsync`, atomic rename, and parent-directory `fsync`. Every broker mutation snapshots the prior v1 state. A commit failure before rename restores that snapshot and leaves durable state unchanged. A failure after rename makes durability uncertain, so the store is poisoned without rolling memory back and the daemon terminates rather than serving with divergent state. Existing state is opened without following symlinks, must be a bounded regular file at mode `0600`, and is fully schema- and invariant-validated before use. Persisted tokens must be unconsumed, match their session's current generation, and remain within the 256-token cap. Session identity is uniquely keyed by `(anchor_pid,anchor_starttime)`; duplicate logical sessions for one anchor refuse startup. State integrity or mode failures refuse startup. The daemon does not log session IDs or tokens.
|
||||
|
||||
@@ -10,9 +10,12 @@ from pathlib import Path
|
||||
|
||||
|
||||
MOSAIC = Path(__file__).parents[2]
|
||||
REPOSITORY = MOSAIC.parents[1]
|
||||
SKILLS = MOSAIC / "framework/skills"
|
||||
REFRESH_SKILL = SKILLS / "mosaic-context-refresh/SKILL.md"
|
||||
GATE_PATH = MOSAIC / "framework/tools/lease-broker/mutator-gate.py"
|
||||
COMPACTION_THREAT = REPOSITORY / "docs/architecture/compaction-revocation.md"
|
||||
RECEIPT_PROTOCOL = REPOSITORY / "docs/architecture/lease-broker-protocol.md"
|
||||
OPERATOR_HOME = re.compile(r"/home/[^/\s]+/")
|
||||
RECOVERY_PLACEHOLDER = "/absolute/path/to/mosaic/tools/lease-broker/recover-context.py"
|
||||
CONSTRUCTION_PLACEHOLDER = "/absolute/path/to/mosaic-context-refresh-construction.json"
|
||||
@@ -59,6 +62,47 @@ class FrameworkSkillPortabilityTest(unittest.TestCase):
|
||||
GATE.RECOVERY_TOOL,
|
||||
)
|
||||
|
||||
def test_t30_dual_hook_miss_matches_the_amended_threat_table(self) -> None:
|
||||
threat_table = COMPACTION_THREAT.read_text(encoding="utf-8")
|
||||
receipt_protocol = RECEIPT_PROTOCOL.read_text(encoding="utf-8")
|
||||
self.assertRegex(
|
||||
threat_table,
|
||||
r"\| Both observers are missed, lease unexpired\s*\|\s*\*\*ALLOWED\*\* inside the bounded residual stale window\.",
|
||||
)
|
||||
self.assertRegex(
|
||||
threat_table,
|
||||
r"\| Both observers are missed, lease expired\s*\|\s*\*\*DENIED\*\* by monotonic TTL expiry\.",
|
||||
)
|
||||
self.assertRegex(
|
||||
threat_table,
|
||||
r"`enable_status_check=False` \(status checks not\s+enforced\)",
|
||||
)
|
||||
self.assertRegex(
|
||||
receipt_protocol,
|
||||
r"detects an \*\*ABSENT\*\* or \*\*PREFIX-TRUNCATED\*\* terminal\s+token",
|
||||
)
|
||||
required_threat_text = (
|
||||
"## T-C server-side branch-protection posture",
|
||||
"`main` is push-blocked and PR-only-merge",
|
||||
"Status-check enforcement and approval enforcement are **RECOMMENDED**.",
|
||||
"## Current-vs-required gap (recorded, not enacted)",
|
||||
"`enable_push=False` (push-block present)",
|
||||
"`require_approvals=0` (approvals not enforced)",
|
||||
"`block_on_official_review=False` (official review not enforced)",
|
||||
)
|
||||
required_receipt_text = (
|
||||
"## Receipt boundary and T-C residual (R1)",
|
||||
"**MIDDLE-DROP** that preserves the tail",
|
||||
"**NOT receipt-detectable**",
|
||||
"server-side protected-branch controls",
|
||||
)
|
||||
for contract_text in required_threat_text:
|
||||
with self.subTest(document="threat-table", contract_text=contract_text):
|
||||
self.assertIn(contract_text, threat_table)
|
||||
for contract_text in required_receipt_text:
|
||||
with self.subTest(document="receipt-protocol", contract_text=contract_text):
|
||||
self.assertIn(contract_text, receipt_protocol)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user