Set flavor on open workplans from origin/prose/status. Copy existing depends_on aliases only. Do not promote residuals. Assistant: grok Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
46 KiB
| id | type | title | domain | repo | status | flavor | owner | topic_slug | created | updated | reviewed_at | reviewed_against_commit | reviewed_note | origin | origin_ref | state_hub_workstream_id |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| INFD-WP-0001 | workplan | Founding specs and approver-UI ownership | infotech | informed-decision | active | planning | claude | netkingdom | 2026-09-09 | 2026-09-11 | 2026-09-09 | ee2cca5 |
Reviewed against approval-engine's INTENT, SCOPE, layer.yaml, APPROVAL-WP-0002 and docs/keycape-service-registrations.md; the accepted NetKingdom security layer model v0.7 and companion v0.2; the repo-classification and project-repository-flavor standards; and the Decision Memo schema, state-transition tables and canonicalization vectors in history/20260909-initial-exploration. Repo-owned specification work can proceed. T05 and T07 remain externally gated on the gate-house ruling requested as INFD-IN-0001; T08 is additionally gated on APPROVAL-WP-0002-T01 and a deployed approval-engine. | founding | history/20260909-initial-exploration/InitialExploration.md | a985a65a-08f7-5a39-8645-b618ea022657 |
INFD-WP-0001 — Founding specs and approver-UI ownership
Establish informed-decision as a governed repository in the NetKingdom estate,
settle who owns the browser-facing approver UI that approval-engine
deliberately does not contain, and produce the specification set that Stage 1
implementation will be built against.
The trigger is concrete and dated. On 2026-09-08 key-cape (KEY-WP-0013-T02)
asked approval-engine for the human approver client's client_id and callback
URI. approval-engine correctly declined to invent them and recorded in
docs/keycape-service-registrations.md that the human approver flow "belongs to
whichever browser-facing approver UI presents approval:approve tokens to this
engine. That component is not in this repo." APPROVAL-WP-0002-T01 remains
progress partly because of it. This workplan closes that gap by naming the
owner and shipping the contract.
Reviewed 2026-09-09; the plan is active. Reviewed against the contracts
listed in the frontmatter. T02 remains the gate for the architecture: if
gate-house places this component differently, T05 and T07 are rewritten before
they are written. T03, T04 and T06 are independent of the ruling because they
describe what the surface must do and what the evidence must contain, neither of
which changes with the catalog row.
Scope boundary for this workplan: specification and contract, plus one
walking skeleton. Full L3 product build is residual and belongs to
INFD-WP-0002.
Establish the founding documents
id: INFD-WP-0001-T01
status: done
priority: high
state_hub_task_id: "abe83f6a-18d2-5fe5-bd01-56b6fd0babbe"
Write the repository's stable statements of purpose and current stage, derived from the founding exploration rather than reinvented.
Acceptance: INTENT.md states purpose, the Decision Memo concept, ownership and
non-ownership against the named estate repositories, design principles, wrongness
conditions and success criteria; GOAL.md states the Stage 1 outcome, exclusions,
invariants and definition of done; README.md orients a new reader in under a
minute; history/20260909-initial-exploration/ is preserved unmodified as the
provenance record.
Completed 2026-09-09: INTENT.md, GOAL.md, README.md, SCOPE.md,
AGENTS.md and .repo-classification.yaml written; repo registered in the
State Hub and INFD-WP-0001 indexed by fix-consistency.
Two corrections made during the same task, recorded rather than silently fixed:
GOAL.mdfirst declaredrepo_flavor: project. That is wrong.project-repository-flavor_v0.1.mdreserves theprj-flavor for bounded cross-repo coordination efforts and states that durable products useINTENT.mdand an ordinary category. This is a durable product;.repo-classification.yamlsetscategory: product.GOAL.mdis retained as the stage statement, which the flavor standard does not forbid.SCOPE.mdwas initially deferred to T06 on the reasoning that a scope file written before the layer ruling describes an imagined boundary. The Repo Manager requires it (C-35), and the reasoning was better served by writing an honestly empty one: the shippedSCOPE.mdstates plainly that nothing is implemented and that T06 rewrites it. T06 now rewrites rather than creates.
Settle layer placement and approver-UI ownership with gate-house
id: INFD-WP-0001-T02
status: done
priority: high
state_hub_task_id: "4f94134b-2260-5404-84e0-12f2b08ef565"
Take the ownership question to gate-house as doctrine rather than asserting a
catalog row. The proposal to argue: informed-decision owns the browser-facing
approver surface, the presentation record and the evidence of informedness; it
is PEP-shaped under statute §6.4 and companion §5 because it is
browser-facing and causes a protected side effect on the far side of a decision;
it supplies exactly one PIP-like fact — what was presented — as a claim, and
never evaluates it.
Raise explicitly, and do not paper over:
- §17 has no request-claim schema owner assigned. A
view_hash-bearing presentation claim must yield to that schema when it exists rather than inventing a permanent local shape. approval-engine's claim already carries "a digest over the same canonical binding the decision point already computes". Whetherview_hashis that digest, a sibling of it, or a distinct presentation attestation is a real question and the wrong answer creates two competing canonicalizations of the same act. This is the single highest-risk unknown in the workplan.- A surface that renders both the question and the answer is adjacent to the
self-dealing objection that kept the object out of
access-engine. State why it is not the same failure: this repository holds no state a decision reads as authority.
Acceptance: an intake is filed with gate-house; a ruling or a recorded decision
exists; layer.yaml is written from the ruling and matches the catalog row, not
this workplan's prose; INTENT.md and GOAL.md are amended if the ruling
differs from the proposal; the view_hash-versus-binding-digest relationship is
recorded as a decision, not left implicit. Blocking for T05 and T07.
Product Requirements Document
id: INFD-WP-0001-T03
status: done
priority: high
state_hub_task_id: "86465a35-1af5-5956-a767-57ad838dffa9"
Write docs/specs/ProductRequirementsDocument.md for Stage 1: the L3 approval
approver surface, scoped to one real consumer.
Must cover: the personas (approver holding a mandate, requester, observer/auditor);
the disposition vocabulary and which verbs are legal on which step kinds; the
required-highlight acknowledgment gate; the pre-sign/awareness split as it
appears in the UI; the evidence bundle as an export; accessibility and locale
(DE/EN, given the Umlaufmappe framing); and the explicit non-requirements from
GOAL.md — no mandate graph, no QES, no notification transport.
State the anti-requirements as first-class: no dark patterns, no dwell timers, no keystroke analytics, and a UI that makes unmistakable that the whole instrument is bound rather than only the acknowledged highlights.
Acceptance: every requirement traces to either a GOAL.md definition-of-done
item or a named external contract; each requirement is testable; the document
names what it is deliberately not requiring and why.
Completed 2026-09-09: docs/specs/ProductRequirementsDocument.md. 30 numbered
requirements, each with a trace: line to a GOAL.md DoD item, an INTENT.md
principle or wrongness condition, a state-transition guard, or a named external
contract, and each with an observable pass condition. Anti-requirements
(PR-70..75) are stated as testable absences: no dwell timers, no attention
analytics, no dark patterns, no auto-approval, no approval-state caching, no
authorization endpoint. Four known limitations are recorded up front rather than
discovered later — chiefly that escalate without a mandate graph is forwarding
(L-01) and that view_hash is computed by the renderer, so a compromised
surface can present X and attest Y (L-02). Four open questions are left for
review rather than answered by assumption.
Use Case Catalog
id: INFD-WP-0001-T04
status: done
priority: medium
state_hub_task_id: "00a83db5-fe0e-522a-b885-6f9ef034bc07"
Write docs/specs/UseCaseCatalog.md covering the full depth spectrum L0–L5, with
Stage 1 scope marked, so that scale invariance is testable as a design property
rather than a claim in a vision statement.
For each level: actor, trigger, requested act, binding level, the verbs that must be available, the evidence produced, and the estate repository that is the counterparty. Include the L0 login banner and the L2 ADR accept in full even though they are out of Stage 1 build scope — their purpose here is to constrain the schema so the object cannot fork later.
Include the negative cases: accept on a Kenntnisnahme step (illegal by design),
a bind attempt with unacknowledged required highlights, an agent attempting a
disposition, and a tenant switch requiring a new bind.
Acceptance: every use case maps onto the single Decision Memo schema with no level-specific object; each negative case names the invariant it protects; the catalog states for each level which estate repository would consume it.
Completed 2026-09-09: docs/specs/UseCaseCatalog.md. Six use cases L0-L5 with
counterparty and stage, ten negative cases each bound to a guard or isolation
vector, and a closing section naming the four changes that would fork the
object — a level-specific status, multiple questions per memo, a per-level
packet model, and an approvals-inbox entity that acquires state. Each case
records what it contributes as a constraint on the shared schema, so the
scale-invariance claim is checkable rather than asserted.
Architecture Blueprint
id: INFD-WP-0001-T05
status: done
priority: high
state_hub_task_id: "20ea118e-0657-5054-8aa2-b316444f4000"
Write docs/specs/ArchitectureBlueprint.md. Depends on T02 — the layer ruling
determines what this component is permitted to be.
Must cover: the component boundary and its position in the security layer model;
the call graph to key-cape (OIDC authorization-code + PKCE for humans),
approval-engine (bearer approval:approve, never approval:consume),
access-engine (decision, never rendered here) and audit-core (evidence
emission); the storage posture for memos, presentations and dispositions;
the deployment shape including the Ingress and external origin that
approval-engine explicitly does not have; and the failure modes — what the
surface does when approval-engine, key-cape or audit-core is unavailable.
Fail-closed is the default and must be stated per dependency. A surface that degrades into showing a memo it cannot bind is acceptable; a surface that degrades into binding without evidence is not.
Acceptance: no component in the diagram renders an authorization decision; the
token audiences, scopes and principal types match approval-engine's
docs/keycape-service-registrations.md exactly; every external dependency has a
stated unavailable-stance; the blueprint names which parts are Stage 1 and which
are placeholders.
Completed 2026-09-09: docs/specs/ArchitectureBlueprint.md. Written after the
ruling, as intended — the layer answer shaped it rather than being retrofitted.
The constraint that did most of the work is GH-DEC-2026-012 limit 3: the
evidence copy must reach audit-core independently of this component, because
here the actor being audited and the evidence source are the same. That is
booked as four binding implementation consequences plus an open item (O-02)
that must be resolved before T08 ships, rather than as a principle — "we will
add the independent path later" is how limit 3 becomes limit-3-in-principle.
Also fixed: presentation/ is the only writer of view_hash; the
approval-engine client must expose no validity cache; a fail-closed outcome is
never recorded as an approver's decline, because the human did not make one; and
the assurance shape is cited from key-cape's contract rather than restated,
so it cannot drift.
Evidence model, schema promotion and canonicalization under test
id: INFD-WP-0001-T06
status: done
priority: high
state_hub_task_id: "47cb3f7a-e349-5c81-a304-86275e058a85"
Promote the exploration artifacts from history/ into governed, tested
repository assets, and write docs/specs/EvidenceModel.md.
Move decision-memo.schema.json to schemas/, canonicalize.py into the
package, and the fixtures in vectors/ into the test suite. history/ stays
untouched as provenance; the governed copies are the ones that change.
The four isolation properties from the exploration become tests that must stay green:
- shuffling object keys does not change either hash;
- editing an awareness field does not change
view_hash; - changing
binding.targetdoes changeview_hash; - selecting a role after login emits
session.hat_selectedand does not rewriteview_hash.
EvidenceModel.md covers what an evidence bundle contains, how it verifies
offline, its relationship to audit-core's archive, and — carried over from
approval-engine's reasoning rather than rediscovered — the honest statement
that a hash chain proves records were not altered after arrival and cannot prove
a record was never sent.
Rewrite SCOPE.md as the last step of this task. The version shipped in
T01 is honestly empty — it states that nothing is implemented. Replace it with
the real implemented-and-first-cut boundary once the layer ruling and the specs
have fixed it, and drop the T01 status banner.
Acceptance: schema, canonicalizer and vectors live outside history/ and are
exercised in CI; the three published expected hashes reproduce byte-for-byte;
EvidenceModel.md states the residual it does not close; SCOPE.md describes
the implemented-and-first-cut boundary rather than the aspiration, and no longer
carries the T01 "nothing is implemented" banner.
2026-09-09 — substantive half done; task stays progress because the SCOPE.md
rewrite is gated on T02. Delivered:
schemas/decision-memo.schema.jsonplus both worked examples;informed_decision/canonicalize.pyas the governed canonicalizer, with the ad-hoc__main__block replaced bypython -m informed_decision; fixtures undertests/vectors/.history/is untouched.tests/test_canonicalize.py— 20 tests, all green. The three published hashes reproduce byte for byte, and all four isolation properties are pinned.docs/specs/EvidenceModel.md.pyproject.toml,Makefile(make test,make check).
Two things worth recording rather than burying:
- Isolation properties 1, 2 and 4 are all negative — they assert the hash does
not change. A canonicalizer returning a constant would pass all three.
Property 3 plus per-field variants over
question,requested_act,binding_levelandpacketare what stop the suite being vacuous. test_governed_vectors_match_the_preserved_history_copyasserts the governed fixtures have not drifted from the founding copies, so quietly editing a vector to make a failing test pass is itself a failure.
Remaining for done: rewrite SCOPE.md after the T02 ruling.
2026-09-09 — done. SCOPE.md rewritten now the ruling and the specs have
fixed the real boundary. It carries a "What this repository does not claim"
section, because a scope file listing only capabilities overstates them: the
decision path is not validated while GH-DEC-2026-010 is open, the residual is
not closed, view_hash is not inside the approval entry, and nothing is
deployed.
Publish the OIDC browser-client contract to key-cape
id: INFD-WP-0001-T07
status: done
priority: high
state_hub_task_id: "38a83a76-f152-55bf-8a9a-6132fd6d9642"
Own and publish the two strings approval-engine could not supply: the human
approver client's client_id and its full callback URI. Depends on T02 for the
ownership ruling and on T05 for the deployment origin.
The registration is an authorization-code + PKCE public or confidential browser
client — not client_credentials — and the resulting access token must carry
aud=approval-engine, principal_type: human, tenant: tenant:platform and
scope approval:approve. Redirect URIs match exactly at /authorize, so the
origin must be a real deployed origin, decided in T05, not a placeholder.
Do not request approval:consume: approval-engine refuses it for human
principals, and consumption belongs to the PEP that causes the side effect.
Acceptance: docs/keycape-client-registration.md publishes both strings and the
expected token shape; the contract is sent to key-cape referencing
KEY-WP-0013-T02, and to approval-engine referencing its
docs/keycape-service-registrations.md follow-up; a token issued against the
registration is accepted by approval-engine's verifier; KEY-WP-0013-T02 is
unblocked. This is the task that discharges the gap that created this
repository.
2026-09-10 — unblocked. INFD-IN-0002 closed: key-cape had already
implemented registration-bound tenancy (329e48f) correct under both candidate
rulings, and GH-DEC-2026-013 granted the shape as a declared bounded gap. Two
obligations land here and are booked as PR-08/PR-09: never use the token's
tenant claim as the act-scope (binding.target is, and already was), and
record the claim's provenance, since key-cape emits it as a bare string and a
consumer cannot otherwise tell a directory-asserted tenant from a
registration-supplied one. Build to the registration-bound shape knowing it is
transitional.
The one remaining input is the deployed origin. Redirects match exactly, so
client_id and the callback URI must name a real origin — the reason not to
publish is now solely that, and no longer the tenant.
2026-09-10: docs/keycape-client-registration.md written and everything except
the host is fixed — client_id informed-decision-approver, callback path
/auth/callback, authorization-code + S256 PKCE public client, scopes
[openid, approval:read, approval:approve], the expected token shape, and the
assurance shape cited from key-cape rather than restated so it cannot drift.
§5 declares the tenant provenance rather than assuming it. Nothing populates
domain.User.Tenant for approver users, so declaring tenant:platform reaches
the token by the gap route by construction, not by accident.
GH-DEC-2026-013 was explicit that a tenant the directory does not carry must
not be declared unless we are prepared to say so in the record. We are, and it
is said there, along with the two consequences: the claim is stored with its
provenance, and it is never used as the act-scope.
2026-09-10 — origin assigned. The operator assigned
decisions.coulomb.social, not the decide.coulomb.social this workplan
proposed. docs/keycape-client-registration.md §2 is corrected to the assigned
name; the proposal is not preserved anywhere a reader could mistake it for the
registration, because a redirect URI that is one character off fails closed at
/authorize and presents as a rejected login rather than a registration
defect. DNS resolves to the cluster address.
2026-09-10 14:32 UTC — the origin is live. railiance-apps applied
manifests/informed-decision-origin.yaml and
manifests/informed-decision-ingress.yaml; cert-manager issued a Let's Encrypt
certificate (CN=decisions.coulomb.social, valid to 2026-12-09) and
GET https://decisions.coulomb.social/auth/callback returns 200 over a
verified chain. The path is served by a placeholder until T08 ships, which does
not affect the registration — key-cape matches the redirect URI as a string at
/authorize and never fetches it. Evidence:
railiance-apps/docs/informed-decision-origin.md.
2026-09-11 — correction to this task's premise. This note and the
registration doc previously said publishing would "close KEY-WP-0013-T02,
blocked since 2026-09-08". Checked against key-cape rather than against this
file's own prose: KEY-WP-0013-T02 is done, and so is KEY-WP-0013-T05, the
task that actually held the human registration. key-cape did not wait — it
published the contract shape from its side (accepting the approval:read scope
gap as ours-was-right, documenting the assurance object, and fixing at from
mint time to authentication time), and closed KEY-WP-0030, a follow-up this
repo prompted. The gap that created this repository is discharged on the issuer
side already. What is left is this repo submitting its half.
Every input this task owns is now fixed and real.
docs/keycape-client-registration.md is ready to submit. The task stays
progress until the remaining acceptance criteria are met, which are not
document criteria: the contract must actually reach key-cape citing
KEY-WP-0013-T02 and approval-engine's
docs/keycape-service-registrations.md, and a token issued against the
registration must be accepted by approval-engine's verifier. That last one
cannot be demonstrated until approval-engine is deployed, so T07 will close
alongside, not before, the deployment T08 also waits on.
Superseded context: the origin was the sole blocker — DNS alone is not an origin, and a host that resolves but does not complete a TLS handshake fails the same way a wrong hostname does, only later and less legibly.
Superseded context: the origin was previously the sole blocker in its unowned form — choosing a plausible hostname is not the same as owning one.
Superseded context: 2026-09-09: blocked on INFD-IN-0002. key-cape found that a human access
token cannot carry tenant:platform today — the tenant claim resolves from a
directory record no adapter populates, so every human token falls back to
tenant:coulomb, which approval-engine refuses by exact match. Registering
the client before this is resolved would ship a login that fails closed at first
use, and the failure would present as a rejected approval rather than as a
registration defect. Position stated (registration-bound) with an
evidence-model reason, and routed — it writes a cross-tenant capability into the
issuer, so it is not this repository's to decide alone.
Walking skeleton — one approval, end to end
id: INFD-WP-0001-T08
status: progress
priority: medium
state_hub_task_id: "b5c1d329-9580-5672-9640-2930cbbb729a"
Prove the specs against reality with the thinnest possible L3 path: sign in via
key-cape, retrieve one named approval by id from approval-engine,
render one as a Decision Memo with brief, packet and highlights, acknowledge the
required highlights, and submit an approval entry with a stored presentation
record carrying view_hash.
return and discuss are in this skeleton, not deferred. They are the
differentiator; a skeleton with only approve/reject proves the wrong product.
Acceptance: one approval is approved by a real human through this surface
against a deployed approval-engine; the approval entry is reconstructable from
a stored presentation; a bind attempt with unacknowledged required highlights
fails closed; return produces a structured reason and is distinguishable from
decline in the record; no code path in this repository evaluates whether the
act is permitted.
Gated externally on approval-engine APPROVAL-WP-0002-T01 reaching done and
on the service being deployed with an origin this surface can reach.
2026-09-10 — domain core built and tested; the live proof remains gated.
approval-engine APPROVAL-WP-0002-T01 is still progress and the namespace
has no pods, so the end-to-end proof against a deployed engine cannot run. Built
everything that does not depend on it, with the engine behind a seam so its
arrival is a wiring change rather than a build:
memo.py— the Decision Memo, versions, and the binding document.Principal,Scope,AwarenessandHatare dataclasses rather than dicts because the canonicalizer requires a shape, and a missing key should fail at construction rather than deep inside hashing. Field names follow the governed canonicalizer (item_id,severity,locator) — the vectors are the contract, so the object was aligned to them rather than the reverse.presentation.py— the sole writer ofview_hash. Acknowledgment is an explicit method call; nothing infers it.disposition.py— the verb vocabulary and guards.acceptis absent from weak steps rather than present-and-disabled, because a greyed-out accept still teaches the wrong model.provenance.py— claim routes (A-16).assert_human_control_dischargeablerefuses a registration-suppliedhuman, so PR-11's limitation is visible at the point of use rather than buried in a document.evidence.py— the local outbox, commitment-only records carrying theGH-DEC-2026-014§4 existence assertion, and a custody-locator guard that rejects secret-shaped values (PR-12).approval_client.py— Protocol plus a fake with the engine's real semantics:409 duplicate_approveris success,409 conflictterminal,503fail-closed, andapproval:consumerefused before a token is ever requested.
87 tests pass, including every negative case in the Use Case Catalog: NC-01
through NC-08, the humanity-provenance guard, the existence assertion, and that
a fail-closed outcome is recorded as a stance application with no verb field —
never as a decline, because the human did not make one.
Remaining for done: the live proof. Superseded context —
2026-09-09: additionally gated on INFD-IN-0003 — GH-DEC-2026-012 limit 3
requires the evidence copy to reach audit-core independently of this
component, and the payload question is open. Design and decision request in
docs/evidence-path-design.md. This task must not ship before it is answered.
2026-09-10 — browser authentication and real HTTP adapter implemented.
oidc.py verifies KeyCape authorization-code/S256 PKCE, browser-bound single-use
state, ID-token nonce and client audience, access-token resource audience,
paired subject/tenant/provenance, exact scopes and MFA facts. Tokens stay in
bounded ephemeral server sessions. web.py supplies the sign-in shell with
secure cookies, CSRF sign-out, no token output and no memo/entry route.
approval_http.py reads by id and submits human entries, requires the declared
control and native digest format, and recovers duplicate correlation from the
actual stored entry. No consume API, validity cache or automatic POST retry.
206 tests pass, including three checks against Approval Engine's actual JWT
verifier/API and 100 existing domain/contract tests. Six installed-entrypoint
HTTP smoke checks pass. These use signed synthetic identity fixtures: no
native human login, audit admission or factory execution is claimed.
The component test caught an unprefixed-digest assumption; the adapter now
carries the native sha256:<64 hex> value unchanged. The package's dev extras
now include PyYAML so layer conformance cannot silently disappear in a clean
installation. Evidence: docs/evidence/2026-09-10-browser-authentication.json.
The earlier principal-type provenance assumption is corrected against KeyCape
f9812ab: its user-authenticated code flow emits human, while client credentials
emit service; tenant_source distinguishes directory and registration.
Unknown tenant provenance fails closed. This adopts the current issuer contract
without assuming that it has been proven on the deployed login path.
Next within this task: durable presentations/dispositions plus transactional
outbox; entitlement before render; connect one named memo with required
acknowledgments, accept/return/discuss and correct actor/presentation binding;
independent audit delivery; then native registered login and deployed-engine
proof. Ambiguous engine POSTs must reconcile against stored entry correlation
before a UI retry. /readyz deliberately remains 503 and browser entry routes
are absent until this protected path is wired. The task remains progress.
See docs/browser-authentication.md.
2026-09-10 — durable evidence and receiver integration implemented.
store.py / records.py persist packet bytes, immutable memo versions and
presentations, explicit append-only acknowledgments, dispositions, submission
correlation and a same-transaction evidence outbox in private SQLite storage.
The new G_ACTOR guard refuses use of another person's presentation. The two
older tests with mismatched placeholder actors were corrected; a dedicated
negative regression now pins the real guard. Return/discuss remain local acts.
Operation ids and atomic reservation prevent double submission. Lost engine responses and unknown duplicates remain unresolved and cannot be attached to a new presentation or silently retried. A confirmed correlation retrieves the original acknowledgment snapshot; later acks do not strengthen earlier evidence. Revisions cannot race in-flight/unresolved submissions. Backup/restore preserves the evidence and pending delivery state.
audit.py uses the current Audit Core ingestion contract, stable event ids and
bytes, scoped metadata-only envelopes, receiver references, retry/backoff and
visible blocked records. Writer-only reconciliation and a separate auditor read
were exercised against the actual receiver. Heartbeats are per class and do not
mask undelivered evidence. The receiver counts by accepted_at, while the source
counts by occurred_at: the report now carries both, and delayed acceptance is
not automatically called a loss. Exact window shape came from the real API,
not from a local fixture assumption.
258 tests pass (52 added), including process death before commit, injected
outbox failures, concurrent clicks/reservations, restore, lost engine response
and receiver deduplication. Actual Approval Engine and Audit Core APIs are
exercised with synthetic identities and development custody. Evidence:
docs/evidence/2026-09-10-durable-review-evidence.json. No native policy, human
login, production audit custody, deployed binding UI or factory run is claimed.
Next within T08: define/admit the exact PDP read/bind request and caller,
then connect protected review/ack/accept/return/discuss routes to this store and
its original-entry recovery rules. Persist the obtained policy observation and
retain decision_attributable=false while its upstream gap remains. No Informed
Decision package/registration was found in Flex Auth's checked examples,
registry or docs at 88b3543; a local allow rule is not a replacement.
Schedule and admit audit draining/heartbeats, account for acceptance-time delay
in reconciliation, supply native custody and registered human/deployed-engine
proof. These remain live work in this task; /readyz stays 503 and browser bind
routes remain absent. Details: docs/durable-review-evidence.md.
2026-09-11 — protected review and actual browser exercise implemented.
The configured 0.2.0 service now connects the named memo to fresh Flex Auth
checks before render, download, acknowledgment and each human response. The
separate workload caller, exact package/version/digest and submitted request
binding are validated; observations are immutable and retain
decision_attributable=false. No presentation claim becomes a policy input.
Actual native component checks cover caller enforcement, request digest wire
format and registry enrichment, with fixture-only assignments and credentials.
The browser supports explicit highlights, accept/return/discuss/decline, current actor/version/session/act checks, original confirmed correlation and visible uncertain submissions without POST retry. An audit thread drains bounded batches, emits declared heartbeats and saves a two-time-base reconciliation snapshot; unhealthy delivery closes acceptance readiness. Owner configuration provisions no identity, policy or custody. Existing v1 databases migrate atomically to v2; old memo UI releases require a new version. The first browser profile is English organizational approval; unsupported locale/step/level refuses before rendering.
344 automated checks pass, including actual Flex Auth, Approval Engine and
Audit Core components. Twelve Chromium checks exercise local HTTPS, synthetic
PKCE login, cookies, entitlement, escaped packet content, acknowledgment bypass,
return, responsive layout, a single accepted entry/reload, audit delivery,
caller refusal and sign-out. The browser caught no-referrer suppressing the
form Origin; review pages now preserve same-origin headers while auth/downloads
remain no-referrer. Missing/null/foreign form origins and bad CSRF still refuse.
Plain-text native caller 401/403 responses also retain their refusal meaning.
Evidence: docs/evidence/2026-09-11-protected-browser-review.json and the
accompanying browser receipt/screenshots. This is disposable component/browser
proof, not native human login, admitted policy/custody, deployment or factory
execution. Factory attempts and paid calls remain zero.
Next within T08: obtain owner admission against
docs/flex-auth-review-contract.md for the exact production package/pins,
caller and assignment source. Admit native review-sender custody with
AUDIT-WP-0009-T11, registration rollout, service packaging/private state,
backup/restore and uncertain-entry operating procedure; then exercise the
deployed callback and actual human binding against the admitted Approval Engine.
German UI acceptance (PR-60), broader product steps/awareness controls and the
specified evidence export remain product work under this live task. They must
not be silently claimed by the bounded factory profile. The exact operational
setup and remaining limits are in docs/protected-browser-review.md.
T08 remains progress; no residual has been hidden by finishing the workplan.
2026-09-11 — container and deployment candidate prepared and exercised.
Containerfile and hashed dependency lock build the installed wheel on the
digest-pinned Alpine base. The runtime has no package installer, runs as 10001
on a read-only root filesystem, and copies bounded projected configuration into
owned ephemeral 0600 storage. It keeps private persistent evidence at 0700/0600
and takes a process-lifetime lock before opening the serving runtime. This
preserves the store's checks despite Kubernetes projected-file ownership and
prevents a second service from sharing its evidence volume.
The installed custody CLI creates SQLite-consistent backups and reports only schema/delivery/submission counts. Eleven real-container checks passed with no external network or published port: restart and restore onto a second volume preserved exact content, undelivered evidence and a synthetic unresolved intent. All created containers/volumes were removed. The full suite passes 371 tests (27 added). Runtime files in the image match all 22 source modules; dependencies match the hashed lock. Trivy returned zero HIGH/CRITICAL findings; its Alpine lifecycle-list warning is retained in the receipt.
The final lifecycle check exposed a real PID-1 shutdown defect: the first image needed a forced kill (exit 137). An explicit SIGTERM handler now lets Waitress drain and the audit pump stop; the final image exits cleanly within 15 seconds. Recreate rollout and the 45-second pod termination window retain one writer.
The renderer prepares a Recreate Deployment, scoped projected caller token, immutable runtime config, PVC/Service and exact network rules. Kubernetes serving health remains separate from audit acceptance readiness, preserving refusal/ recovery access during an audit outage. The source now includes exact ingress proposals for Approval Engine and the admitted PDP, because the existing caller rules do not automatically admit this pod. No namespace-wide caller label is added and no Secret, RBAC, policy assignment or Ingress is created.
Seven objects passed server dry-run in their exact namespaces. The eighth, Approval Engine ingress, found its namespace absent. Its unchanged policy shape passed in a representative existing namespace; that is schema validation only. Observed Traefik label/websecure port, KeyCape public IP and local-path storage were used to make the candidate concrete, not to claim CNI or custody proof.
Evidence: docs/evidence/2026-09-11-container-candidate.json; operating packet:
deploy/README.md. The image is local and unpublished. T08 stays progress for
the admitted native policy/caller/assignments, AUDIT-WP-0009-T11 custody, image
publication, owner service/namespace and exact peer admission, registration and
real human/deployed binding, platform backup/restore and product acceptance.
No cluster apply, native secret read, factory attempt or paid call occurred.
2026-09-11 — native probe exposed fsGroup startup incompatibility.
The native synthetic sender Job failed during private-store bootstrap, before
any audit HTTP request. A filesystem regression reproduced the same refusal:
Kubernetes fsGroup-style setgid volume roots cause mkdir(0700) to create a
2700 child, which the evidence store correctly refuses. private_directory
now clears inherited setgid only on a directory this invocation just created,
through a non-symlink directory descriptor. Unsafe existing directories keep
their mode and remain refused. The private evidence contract stays 0700/0600.
The new regression failed before the correction and passes after it; existing
2700/2770 directories remain unchanged and refused. The available full suite
passes 335 tests, with 39 optional checks skipped in this environment. The native
sender retry consumed this exact corrected helper and passed ingestion,
restart/duplicate and scope/read refusal checks. The rebuilt local image
sha256:92cde609fca63141740d53594a80a513e250a4d68cdde98b973a9de66403d8c8
passes eleven container checks, including verified setgid-volume startup,
restart and restore. The fixture now asserts the volume's actual 2770 mode;
its one-shot volume initializer explicitly retains FSETID for that setup only.
The service container still drops all capabilities. Independent archive
readback, bearer-revocation acceptance, image scan/publication and cutover remain.
Evidence: docs/evidence/2026-09-11-factory-native-producers.json.
T08 stays progress for native acceptance and the existing deployment/human
binding/recovery gates; no service cutover or human disposition occurred.
Known risks
- T02 is a hard gate. Writing the blueprint before the layer ruling risks building a component the statute does not permit in that shape.
- Two canonicalizations. If
view_hashandapproval-engine's binding digest are not reconciled in T02, the estate ends up with two hashes over the same act and no rule for which one is authoritative. - Deployment origin is on someone else's critical path. T07 cannot complete without a real external origin, and this repository does not yet own an Ingress. This is the most likely cause of slip.
- Scope pressure toward an approvals inbox. The fastest way to close
KEY-WP-0013-T02is to build a queue with two buttons. That would satisfy the dependency and abandon the thesis. T04 exists to make the cost of that visible.
Session note — 2026-09-10, both blockers cleared
T07 origin: cleared and independently verified. railiance-apps deployed
decisions.coulomb.social at 14:32 UTC (their 7c2e51a) and corrected the
hostname in this repository's docs/keycape-client-registration.md and the T07
note — the assigned name is not the decide.coulomb.social this workplan
proposed. Verified here rather than taken on report: / and /auth/callback
both return 200 from 92.205.62.239, TLS verify 0, Let's Encrypt
CN=decisions.coulomb.social issued by YR2, valid to 2026-12-09. The path is an
nginx placeholder, which does not affect a registration matched as a string at
/authorize.
T08 evidence path: cleared. audit-core registered this source with every
field as proposed (AUDIT-IN-0003, their c4016a7) and landed the detection
half (AUDIT-WP-0009 T04/T06/T07). INFD-IN-0003 closed.
INFD-IN-0004 ruled — GH-DEC-2026-015, and gate-house reversed itself.
Nesting is permitted for this pair. The decisive ground was not the cycle
argument we led with: our binding slice canonicalizes principal and target,
two of the five digest fields, so co-reference left us performing a partial
recomputation of one act in a second vocabulary — closer to the translation R3
forbade than nesting is. Our ordering-dependency objection was withdrawn as
mistaken. The permission is conditioned and NOT ACTIVE: it turns on when
approval-engine states its presentation exclusion as normative and tested.
layer.yaml is deliberately unchanged and carries
nesting_permission_active: false. We do not activate on our own initiative.
GH-DEC-2026-016 ruled NC-03. Where an approval is declared as discharging
a human-in-the-loop control, the approver must be human and approval-engine
must refuse at bind time. Our surface enforcement stays — ours refuses earlier
with a better error, theirs makes the refusal a property of the object. Its §5
lands here as PR-11 and is live rather than hypothetical:
principal_type: human is a property of the client registration, the same
shape as the gap-route tenant, so a human-in-the-loop control must not be
discharged on it as verified humanity.
A-16 and A-17 were corrected with this repository's rider and precondition
(gate-house@62c6399), including the dependency that A-17 needs A-16 first.
T07 — done
Submitted to key-cape 2026-09-10, citing KEY-WP-0013-T02:
client_id informed-decision-approver
redirect_uri https://decisions.coulomb.social/auth/callback
Both fixed and stable. The submission carries §5's tenant-provenance declaration
— tenant:platform reaches the token by the GH-DEC-2026-013 gap route by
construction, not by accident — and raises PR-11's provenance question about
principal_type back to key-cape, since GH-DEC-2026-016 §5 now depends on
that claim in a way it did not last week.
This is the task that discharges the gap that created this repository.
Step 3 — proving a token against approval-engine — is T08's and waits on
APPROVAL-WP-0002-T01 and their deployment.
heartbeat_classes sent to audit-core: all three classes at 86400, with the
reasoning for declaring one on presentation despite their guidance putting it
outside — at Stage 1 that class behaves like a low-volume one, and a class quiet
because nobody used the surface is indistinguishable from one quiet because the
emitter is broken. Offered for them to overrule.
The activation condition for GH-DEC-2026-015 was relayed to approval-engine:
state the five-field set as normative and add a test that fails if the digest
input set changes. Their reasoning already exists in substance; what is missing
is that a contributor can make that change today with nothing stopping them.
T03 scoped review deployment — 2026-09-14
The operator explicitly admitted net-kingdom-admins as the human review group
for only the T03 apply, verify and read-only key-check records, separately from
its credential-reader membership. The review service is live and ready at
https://decisions.coulomb.social with verified KeyCape groups, fresh MFA and a
dedicated caller-bound Flex Auth policy. Its mandate does not grant consumption.
The new secrets-engine-requester client has subject secrets-engine, tenant
tenant:platform, role secrets-engine-requester, and only approval:create.
CCR-2026-0024 and CCR-2026-0025 provide distinct verifier and attended reader
custody. Native signature, subject, scope and TTL checks passed; excess scopes
and a wrong secret were refused. Existing consumer identity is unchanged.
Three real requested approvals were created with human control, required count
one, and zero entries. Platform evidence is
docs/evidence/2026-09-14-t03-native-approval-requests.json.
Review image: sha256:8f55bcecf37a8d65f96e073510b1ffb4636c0a91d75e1ee7d582ad4bce8b953a. Policy image: sha256:c9f028b49dfcede930a9cc48757ec8371ecc71d20b1bfee2733e55298dffcc7c. Review tests: 339 passed, 39 optional integration tests skipped; 11 container checks and HIGH/CRITICAL image scan passed. Policy checks: 57 local and 6 native caller checks passed, using synthetic subjects, not human binding evidence. Approval Engine CPU request was reduced from 25m to 10m after observing 1m use; review requests 20m and its PDP 5m. Limits are unchanged. Native services ready.
Remaining T03 gate: the operator's exact signed-in account is needed to address three prepared immutable memos, followed by real acknowledgements and acceptance in Informed Decision. No human entry or consume has been generated by an agent. Then execute claim -> validated PDP Check -> CAS consume separately for apply, verify and exec using scoped attended authority, and capture native denial, revocation, workload health and key-check evidence. No OpenRouter credential has been read and no inference or spend was performed. T03 remains waiting; this entry supersedes earlier statements that requester or group admission is missing.
Live browser registration correction — 2026-09-14
The user's login exposed invalid_profile_usage: unknown client_id: the public
informed-decision-approver registration existed in the source example but was
absent from live KeyCape. Applied exactly the existing admitted registration,
with UID/resourceVersion guards and byte-preserving insertion; unrelated clients,
configuration, signing key and pinned image were preserved. KeyCape is ready.
The actual review-site /auth/start now redirects through KeyCape to
auth.coulomb.social. Wrong redirect, consume scope and absent PKCE are refused.
Receipt: key-cape/docs/evidence/2026-09-14-informed-decision-browser-registration.json.
Repeatable contained helper: key-cape/tools/register-informed-decision.py
(default preflight; --apply mutates only a missing exact registration).
Human callback/MFA/token proof and T03 approval entries remain pending. The
previous ready check established service health, not browser login acceptance.
Credential-renewal scheduling recovery — 2026-09-14
KeyCape returned 503 after repeated 10m factor-renewer Jobs could not schedule at full cluster CPU requests. The reviewed service used 1m; lowered its request from 20m to 5m, retaining the 500m limit and memory settings. The next scheduled renewal completed, self-revoked, and propagated through ESO to KeyCape; all four readiness checks and both OpenBao reader callback routes passed. Renderer tests: 14 passed. Receipt: docs/evidence/2026-09-14-keycape-renewal-capacity-recovery.json. Recurring-job scheduling headroom remains with CUST-WP-0071-T01. Human reviews remain confirmed; service restart does not require their repetition.