Mabadiliko
Nini kipya.
Kila kuboresha jukwaa, kama ni meli.
· DocsService
Tell the member — assigned, voided and completed
ONE hr.package_assigned per member per package, never one per document: that is what suppressInvite exists for, and 8 documents x 200 members is 1,600 envelopes for 200 people. The send is one contiguous, transaction-free phase AFTER the assignment rows and the envelopes, because EmailService's send is blocking HTTP into blocking SMTP and interleaving 200 of those with envelope minting stretches a delivery HR is watching a progress state for. Deliver's repair pass and a repeat Deliver both rebuild the same notices and both are refused at the ledger claim rather than by an if — the repair pass exists precisely to re-walk members who were already assigned, so an in-code condition would have to encode "already assigned but possibly not yet emailed", which is the state the ledger already knows. hr.package_voided goes out only for the VOIDED outcome. SKIPPED_EXECUTED must not email — that member finished, and telling them their completed package was withdrawn is both wrong and alarming — and NOT_FOUND must not either, since the id was never this org's and the void service already refuses to resolve a name for it so it cannot become a cross-tenant oracle. Dispatch runs through LegalAfterCommit onto the delivery pool: a serialization retry that rolled the void back would otherwise have already told 200 people their package was cancelled, and there is no unsend. hr.package_completed is a receipt to the member carrying attestedAt and the manifest digest. Called out deliberately: the scope sentence lists assigned and voided, but a canonical template nothing sends is dead weight, and until the filed artifact is downloadable this receipt is the only record the member holds.
· DocsService
Outbound email client and a Redis notification ledger
Idempotency with NO schema change. The reminder bucket is a pure function of (dueDate, today) and never of "when we last sent", so the only durable state a notification needs is a per-(assignment, bucket, dueDate, channel) claim — and SET NX in the Redis DocsService already runs (WopiLockService, KSessionService) has exactly the semantics of the UNIQUE constraint the design asked a table for. It is atomic across the RollingUpdate replicas, the due date is IN the key so extending a due date legitimately restarts the ladder, and it leaves the sweep entirely READ-ONLY against CockroachDB: no write transaction is ever open while EmailService blocks on SMTP. Claim policy is at-most-once by default. An observed failure shortens the claim's TTL so the next tick retries; a crash between send and mark does NOT retry, because a missed reminder is recovered by the next bucket and a duplicate compliance email to every employee of every tenant is not recoverable at all. Redis being unreachable refuses the send rather than sending unclaimed. packageTitle and voidReason are HTML-escaped Java-side: TemplateRenderer is a flat regex replace with no escaping, so HR-authored prose otherwise reaches every employee's inbox as markup, sent from the org's own verified domain. Every varying sentence is pre-rendered as one whole-sentence variable because there are no conditionals and no pluralization either. Persistence is one seam (LegalNotificationRecorder) writing the Redis ledger plus one structured legal-notify line. The durable evidence table is REPORTED in the plan, not invented: a new shared-lib entity is a KamoInitializer run across every consumer.
· DocsService
DocsService's first inbound internal-auth filter
DocsService has no service-to-service authentication: ResourceServerConfig is anyRequest().permitAll() and OTKPreAuthFilter CONTINUES the chain when X-OTK is absent. Authorization is per-controller — an HR right on LegalPackageController, identity on LegalMemberController — and both need a session a daemon does not have. A mapping that fits neither funnel is therefore fully anonymous through APIService's /api/docs/** forward, so the filter ships BEFORE the endpoint it guards, not after. Copied from **************** including the two behaviours that matter: an unconfigured internal.auth.secret answers 503 rather than passing through (reading "no secret" as "no check" is how a control becomes a comment), and the comparison is MessageDigest.isEqual, which does not return early on the first differing byte the way String.equals does — against a freely callable endpoint that is a byte-at-a-time oracle for a cluster-wide secret. Ownership is segment-exact rather than a bare prefix, so /api/docs/legal/internal and everything under it is guarded while an unrelated future sibling is not 403'd by accident. The property is internal.auth.secret (public-chat), NOT mlos.internal-auth-secret.
· kamo-shared-library
Four canonical HR legal-package templates
hr.package_assigned, hr.package_reminder, hr.package_voided and hr.package_completed, as {json,html} pairs plus four CANONICAL_KEYS entries. The CONSTANT is not the templateKey and that seam is where the live QUOTE_SENT bug lives: the loader derives the FILENAME from the constant **************** and reads templateKey out of the JSON, while EmailTemplateService.sendToUser looks the org row up by the key the CALLER passed and, on a miss, seeds rows keyed by the JSON value and retries the same caller key. QUOTE_SENT -> quote-sent.json -> commerce.quote_sent therefore seeds a template no caller can reach. HrPackageTemplateTest pins all three legs for the four HR keys, and pins a file pair for EVERY canonical constant. Rendering is flat {{var}} regex substitution with no escaping, no conditionals and no pluralization, so every sentence that varies is pre-rendered Java-side as one variable (documentSummary, dueLine, duePhrase, reasonLine, receiptLine) and the tests refuse {{#if}}/{{/}}/{{^}}. Every body names {{teamMemberLabel}} and none contains the literal "Team Member": the org's label does not reach EmailService any other way — sendToUser injects only orgName, logoUrl and logoSimpleUrl. No entity, no repository, no enum, no schema. pom.xml stays 1.5.0.
· kamo-internal
Signature button with a live legal-package badge
Hand-written into the fixed left cluster between My Profile and Log out. NAV_REGISTRY cannot express a position between two fixed controls and its badgeKey field is dead code, so this is not a registry entry. Gated on getMemberType() === 'TEAM_MEMBER', never on checkRight(): checkRight returns true for every right under active god mode, and this button leads to the evidence of what a specific person personally agreed to. getMemberType() defaults to 'MEMBER', so the gate fails closed. Badge idiom copied from Chat Now — 99+ cap, color="error", invisible at zero. Icon-only with an aria-label, and navigation through <Link component={NextLink}> because check-client-navigation.mjs fails the build on window.location.href. The tooltip reuses hr.tabs.legalCompliance, the same key the member page heading already calls, rather than inventing one.
· kamo-internal
Live pending-package count over the EXISTING chat socket
LegalPackageCountContext consumes **************** rather than opening a fourteenth STOMP connection for one integer, and subscribes to **************** — the destination MediaService relays legal.packages.{memberId} onto. The REST seed is mandatory, not an optimisation: that subject is core NATS with no stream, so a frame published while the socket was down is gone. Seed on mount, on every reconnect, and on a 60s floor. With no nats block in the DocsService configmap — today's state — those three paths ARE the feature. Everything testable is a pure function in app/lib/legalPendingCount.ts; vitest runs in node and collects only .test.ts, so a context test would not run. parsePendingCount never throws (an exception in a stompjs handler takes the frame loop down and every other subscription on the shared client with it) and refuses a payload addressed to another member, as defence in depth behind the MediaService SUBSCRIBE guard. Ids are compared as strings and never coerced: getMemberIdString(), not the lossy getMemberId().
· MediaService
Relay the legal-package count and guard per-member SUBSCRIBEs
legal.packages.> -> **************** core NATS, no stream — the same shape SupportStompRelayController and PlatformStompRelayController already use. topicFor is a package-private static so the contract between DocsService and kamo-internal is testable without a broker, and it refuses a multi-token tail so the guard only ever sees single-token member ids. The guard is the other half, and it closes a real gap: only /topic/social/feed/{orgId} and /topic/leads/rt/{orgId} were checked, so every per-member topic this broker carries was subscribable by any authenticated member. Relaying a per-member compliance count onto an unguarded topic would have let any employee watch a named colleague's backlog. The decision moves into a pure StompSubscribeAuthz.allow so it can be tested at all — the interceptor is anonymous inside a @Configuration. Both existing org rules are pinned by test before the move, and the function stays an allowlist of guarded prefixes: eleven other topics run unguarded today and flipping the default would take chat, presence and unread down together. Comparison is String.valueOf on both sides. This service stores memberId as a Long and EmailService as a String; a guard copied across without that silently never matches.
· kamo-internal
Proxy the member's pending-package count
A static segment sibling to [uid] so App Router matches it first. PATH ONLY — forwardToApi owns the query string — and one upstream call, because the OTK is single-use.
· DocsService
Serve the caller's own pending-package count
GET **************** through memberGuarded() and the identity funnel. No memberId and no orgId parameter — both come from the session, because an id in a path is an id somebody can change. Mandatory, not optional: legal.packages.{memberId} is core NATS with no stream, so a frame published while a member's socket was down is gone. This is the only correct value after a reconnect. The response shape is IDENTICAL to the pushed frame, so the client parses both through one function and the seed cannot drift from the push. memberId is a string; count is a number.
· DocsService
Emit the badge count on every transition that moves it
Four sites: delivery (none -> PENDING), void (open -> VOIDED), redelivery (open -> SUPERSEDED) and finish (open -> EXECUTED). Each passes the status it read BEFORE the write and the one it wrote, and LegalPendingCountTransition decides — so the rule lives in one testable place instead of four. Two status writes deliberately do NOT publish. PENDING -> IN_PROGRESS is stamped every time a member opens a package and both states are OPEN, so it would put a STOMP frame (logged at INFO with full payload) on the wire per page view. The filer's EXECUTED <-> EXECUTION_INCOMPLETE cannot cross the boundary at all: its FILEABLE set is exactly those two. Finish into AWAITING_OTHERS publishes nothing by the same rule. The member signed, the instrument is not complete, and clearing the badge would say otherwise.
· DocsService
Publish the member's open-package count to core NATS
Kuunganisha. kuchapisha juu ya kisheria.packages. pins huduma hiyo.expectedStream (CHAT MESSAGES) katika kuchapisha na kuchapisha, hivyo JetStream kuchapisha juu ya suala la kisheria ni kukataa na server na kila hesabu update itakuwa kutoweka kimya. Hesabu ni kusoma ndani ya shughuli caller (Hibernate auto-flushes) Kabla ya swala la hesabu, kwa hivyo thamani ni thamani ya baada ya mauzo) na tu Utaratibu huu unarudiwa kwa njia ya kisheria. Baada ya kusoma kwa makini, itakuwa Uliza shughuli iliyokamilishwa; rollback haipaswi kushughulikia kwa sababu Kamwe usiingie kwenye moto. Hakuna chochote hapa cha kujivunia. Baada ya spring, mmea huanza kukauka. simu, na broker aliyekufa haipaswi kugeuka mwisho kwamba tayari aliandika Kuthibitisha tu katika 500. natsConnection inahitajika = uongo na ni null katika uzalishaji leo — Docs Service haina nats block katika configmap yake - Kwa hivyo, mwandishi huharibu bila-op na mbegu ya REST hubeba bege. Kushoto ni Mbunge wa Jimbo la Ludewa Mhe. Huu ni wakati wa pekee. Max SAFE INTEGER na kivinjari hulinganisha hii na JSON.parse.
· kamo-internal
Kutoa Kunyongwa-package kukarabati button halisi
POST **************** existed upstream and was tested, but nothing in the product could reach it: no proxy route, no helper, no button. Every failure branch in LegalExecutedPackageFiler is designed around "HR repairs it" — a flatten that never landed, bytes that drifted since delivery, a subject below Team Member, a conversion mark that failed — and the Assigned Packages grid rendered all of them as an "Error" chip with nothing beside it to click. The action sits in the slot Download vacates, so a row shows exactly one of the two. canFileExecutedPackage mirrors the backend's FILEABLE list exactly, so no button can exist whose only outcome is a 409, and it keys on executedImgId being absent rather than on status alone — an EXECUTED row whose artifact never attached is the silent shape, the one case an operator would never think to retry. The upstream status and reason are preserved end to end rather than flattened into "Load failed": 409 here says WHICH repair is needed, and "that member's security level is below Team Member" is something HR fixes and files again while "a document is still missing" is not. PATH ONLY through forwardToApi, one upstream call for the single-use OTK, existing i18n keys only (retry/success/error, all three verified present and non-empty in all 22 locales), ids stay strings.
· kamo-login
Nenda kwa mwenyeji wa kuingia ili kupona kunaweza kutatua org
Password ahueni sasa anapokea mwanachama wa msingi mailbox anwani, ambayo tu Andika jina la akaunti ndani ya org. Wakala huu unafikia huduma ya usalama juu ya nguzo DNS, hivyo Jeshi ni jina la huduma na mwenyeji wa kivinjari anapaswa kusafiri katika X-Forwarded-Host - kiongozi pekee wa nyuma anaisoma kutoka. Kwa mfano, "A" alias iliyosajiliwa, hivyo login.acme.com inatatua kwa acme.com org as-is; hakuna Andika upya kwenye api. *, ambayo ni mahitaji ya lango la API na njia hii ya kupita.
· SecurityService
Accept a member's primary mailbox address as a login identifier
The address a member gives out is usually the org mailbox flagged Primary on their member settings, not whatever is on their user account — so typing it into the login form failed, and forgot-password for it silently did nothing. Both now fall back to it, after the account username/email has had its say, so account credentials still win and no existing login changes meaning. A mailbox address only names an account within one org, so the fallback is scoped to the org resolved from the login host; login already did that resolution, which now lives in OrgHostResolver so recovery can share it. The account query re-runs by resolved id, leaving every gate — disabled, locked, expired, membership active, password — exactly where it was. An address that names two accounts resolves to neither. The recover proxy in kamo-login has to forward the browser's host for any of this to reach the backend; EMAIL_NOT_VERIFIED now carries the userId, since the verification hand-off could no longer re-find the account from what was typed.
· kamo-internal
Download the executed package from the grid and the member's own page
Pande zote mbili zinapitia mwisho wa LEGAL, zilizowekwa kwenye kazi, sio </api/imaging/download/{imgId}: Njia hiyo inahitaji shirika. wa wa wa wa (ii) Kukagua na kukagua kiwango cha usalama katika eneo la kuingia, na Design §8 ni wazi kwamba DocManager ni nakala rahisi na si upatikanaji njia ya. utekelezaji ImgId anakaa ishara ya uwepo - hatua ni ABSENT, si walemavu, wakati hakuna kitu kimewasilishwa, kwa sababu kifungo cha kupakua ambacho 404s inasoma kama nyaraka iliyopotea. kaziStatus kuacha ramani EXECUTION INCOMPLETE kwa hali ya kawaida.incomplete, ambayo yameelezwa katika kifungu cha pt. Hii ni kwa sababu hakuna kitu katika ← UKURASA WA 6; Kitu Cha Kwanza Unachoweza Kufanya Na Ufanye. → Mwanachama wa Kireno angeweza kusoma "kamili" juu ya mfuko bila mabaki Nyuma yake, katika rekodi yake ya ajira. Jina la faili limejengwa kutoka kwa data na sanitised: kichwa cha mfuko ni maandishi ya bure HR admin typed, na inafikia wote kichwa cha Maudhui-Kufafanua na mtumiaji Mfumo wa faili, ambapo "/" hufanya njia na nukuu inamaliza kichwa.
· kamo-internal
Draw non-image attachments with the document manager's file-type icons
.docx au .xlsx ilikuwa muhtasari wa kijivu wa MUI na barua ya ugani Na kwa upande mwingine, ni chini ya kiwango cha juu, na ni chini ya kiwango. icon faili sawa anapata wakati nchi katika DocManager. Majibu ya faili-icon kwa kuongeza kupitia defaultStyles hasa kama DocManager anafanya, hivyo attachment Hii inaonekana sawa katika rasimu kama hati inavyokuwa, na bila kutambuliwa Uendelezaji unarudi kwenye karatasi sawa ya generic.
· DocsService
File after Finish, repair from HR, and download BY imgId
Filing is queued AFTER COMMIT and OFF the request thread. Finish is @Transactional in a service that also serves WOPI; holding a connection across N ESigService reads, a PDFBox merge and a MinIO write is how a pool of ten starves. Submitting from inside the transaction would also race the commit, which is the failure LegalAfterCommit was written for. A failure in the worker is not a failure of Finish: the attestation is written and the row falls to EXECUTION_INCOMPLETE. Downloads resolve by imgId through the legal endpoints, never through POST /api/imaging/list — that pages org-wide BEFORE filtering assocObjectId, so a member's own package silently vanishes past 100 rows at assoc 1, which the lead Files tab shares. And never through ImageService.downloadDocument either: that checks the CALLER's stale-able security level, and design §8 is explicit that the assignment record is the access path and DocManager the convenience copy. Three mappings, three authorizations: the member's is identity, HR's read is the HR read right, and HR's file action is the WRITE right because it uploads a document into someone's library. The file action is synchronous — an admin clicked it and "that member's security level is too low" is the answer they need, not a 200.
· DocsService
File the merged executed package into the member's own library
uploadDocument takes the SUBJECT'S Member, so Img.organization and Img.createdByMember derive from them and HR ends up having filed a document the member owns. Assoc is SERVICE_APPLICATION — ORDINAL 1, what /settings/member/{id}?tab=docs renders — with assocObjectId = the member id, NOT My Docs. accessLevel is the int 2 and never null; description is the package title; both hashes are computed here because uploadDocument recomputes them and rejects a mismatch. The ImgDat is marked converted. uploadDocument does no conversion and no thumbnails: without the flag the inline-PDF endpoint answers 202 "Conversion in progress" forever. Thumbnails are deliberately absent — the DocManager grid shows a file icon instead of a page preview for this row, and rasterising pages is ConversionService's pipeline, not this one. Filing is REFUSED when the subject's security level is below Team Member. ImagingController skips a too-high document silently and answers a clean empty list, so filing at level 2 for a member at MEMBER(1) would hand them nothing over their own employment agreement. It is refused when a signable document's flatten never landed and when an acknowledge-only document's bytes drifted: a package missing a signature page, or carrying bytes the member never read, is worse than no package. All three leave EXECUTION_INCOMPLETE, which is exactly what that status was declared for. Idempotent by executedImgId, transactions explicit, and every network and MinIO call outside all of them.
· kamo-internal
Say what is too big, instead of "Failed to send email"
A draft that outgrew the send endpoint's limits produced one flat line of red text after the whole payload had been uploaded — or, when the body itself was the problem, nothing until the send failed. Three things decide what a member does next: which piece is too big, how far over it is, and what to do about it, so the notice states the size, draws the overage, and names the fix. SendLimitNotice is a sibling of ComposerNotice and is built from the same tokens, but answers a different question: nothing was rejected and there is no file to point at — the message itself has grown too big. Its bar is the whole message and the dark tail is the part that does not fit, because byte counts alone don't say whether to drop one image or half the draft. Any real overage is drawn at a visible minimum; a tail thinner than a pixel would say the opposite of the sentence above it. The check runs before the upload starts. The server cannot answer usefully once it does: an oversized part is refused in the servlet container ahead of any handler, and on a large overage Tomcat drops the connection rather than swallow the rest of the body. Its 413 is still honoured as a backstop, with its numbers preferred over ours, for the case where the two sets of limits have drifted apart. The notice does not auto-dismiss — it is a blocking condition, not a status, and four seconds is not long enough to read it and act. Also carries the concurrent composer/attachment work in progress, at the user's request.
· kamo-internal
Gate each Human Resources tab on its own right
Overview needs VIEW_OVERVIEW, Attendance MANAGE_TIMECARDS, Legal & Compliance MANAGE_LEGAL_AND_COMPLIANCE, Training MANAGE_TRAINING and Resources MANAGE_RESOURCES. Each tab's gate is derived from that one name, so the gate a tab is hidden by and the one it is refused by cannot drift apart. The HR nav option — and with it the home launchpad card and the command-palette entry, which both render from the registry — now appears only when the HRS app is on for the org AND the member holds at least one of the five. It used to admit any of the eleven HR record rights, which offered the module to members every tab of it would refuse.
· DocsService
Enforce MANAGE_LEGAL_AND_COMPLIANCE on the package endpoints
LegalPackageAccess mirrors the Legal & Compliance tab gate, and that tab is now gated on one right of its own instead of VIEW_EMPLOYEES || EDIT_EMPLOYEES || MANAGE_HRS_SETTINGS. A server set narrower than the tab's leaves a 403 toast over an empty grid; wider, and the endpoint is reachable by someone the UI never offered it to. Read and write are the same right because the right says manage — which is why the SecurityService migration seeds it from the old write pair only, so nobody gained publish-and-void on upgrade. Viewing employees no longer dumps the roster through audience-preview.
· SecurityService
Kubeba wamiliki wa HR zilizopo kwenye haki mpya za kila-tab
A right that gates something already reachable is a removal of access: new rights insert as NOT_SPECIFIED, so the /hr tabs would have vanished on deploy for every HR administrator whose role is not full-access. NEW_RIGHT_SEEDS declares each new right's lineage — the OR-group its tab was gated on before — and it is applied wherever a grant can live: at insert time for org roles and security-model templates (mirroring FORCE_GRANTED as FORCE_GRANTED), and via seedOverrideRights() for job titles, departments and member profiles. Those three tables hold a row only where somebody set one, so insert-where-absent would resurrect a cleared right on every boot. The marker is org_role_rights: a right with no row there has never been seen by this database, which is true exactly once. MANAGE_LEGAL_AND_COMPLIANCE deliberately seeds from the old WRITE pair only, so no read-only viewer was handed publish-and-void by the upgrade.
· kamo-shared-library
Haki za binadamu kwa mujibu wa Human Resources
Vichujio vya / h vilikuwa vimefungwa popote ambapo haki ya HR ya awali ilitokea kuwa AU-term katika mwisho wa mbele: Kuhudhuria kufunguliwa juu ya VIEW SCHEDULES, Mafunzo juu ya Picha zote na Wizara ya Mambo ya Ndani ya Nchi. .................................................................... Hata hivyo shirika hilo halikuweza Malipo ya ruzuku bila kutoa pia juu ya Sheria na Utekelezaji. Picha zote na Wizara ya Mambo ya Ndani ya Nchi. .................................................................... [TD="width: 456"] [FONT=&](2)[/FONT][FONT=&]Bila kuathiri masharti ya kifungu kidogo (1) cha kifungu hiki, Tume itakuwa na mamlaka ya kuajiri mtaalamu yeyote kwa ajili ya shughuli maalumu au kwa muda mfupi.[/FONT] [FONT=&](3)[/FONT] Kiwango cha Rekodi Haki za HR bado zinatawala kile kichupo kinaweza kuonyesha na kubadilisha.
· kamo-internal
Weka mipaka kabla ya kutuma
Mwili uliobeba msingi wa Inline64 - HTML iliyotokana na ukurasa wa wavuti, ukurasa wa mbele Maudhui yaliyonukuliwa, picha za saini - zinaweza kupitisha dari ya sehemu ya kila juu yake mwenyewe. the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the the a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a kumeza mwili wote, kwa hivyo mtunzi alikuwa na upakiaji ulioshindwa na hakuna chochote Kuwaambia kuhusu hilo zaidi ya "kushindwa kutuma barua pepe". CheckSendPayload inapima JSON halisi ambayo inaendelea kwenye waya, pamoja na Viambatanisho na uundaji wa sehemu nyingi, dhidi ya mipaka hiyo ya Huduma ya Barua pepe utekelezaji. Msomaji sasa anasoma jibu la MESSAGE TOO LARGE na mipaka ambayo kutumika, hivyo seva ambayo mipaka yake imehamia bado inaelezewa kwa usahihi Badala ya kukaa kimya kwa muda mrefu.
· DocsService
Hati ya mfuko wa kunyongwa na kuingizwa
Hati hubeba mfuko, toleo la waliohifadhiwa, somo, wakati Katika kesi hiyo, mshitakiwa wa pili anatuhumiwa kwa kosa la uhaini, na Mstari wa WORM hubeba - hivyo mabaki na mstari wa risasi unaweza kuchunguzwa dhidi ya kila mmoja. Hakuna database katika chumba. Kila la heri kwa Watanzania. PDFBox ya Standard-14 fonts encode WinAnsi tu na kuonyesha maandishi juu ya kitu kingine chochote; majina ya mfuko, majina ya wanachama na maandishi yaliyoandikwa ni maandishi ya bure, hivyo cheo cha Kijapani au apostrophe curly Kutolewa nje ya Neno itakuwa vinginevyo sababu mfuko wa mwanachama haiwezi kuwa zilizosajiliwa. Wahusika wasio na uwezo hubadilishwa kwa kuonekana, kamwe huondolewa. Kila kitu kila kitu paginates: Waandishi wa HR vifungu vya urefu wowote na hati ya 14 kwenye pakiti Ni ya kawaida. Muungano unakataa Muungano. KuunganishaPdfs huruka hati isiyo ya kawaida na preends Ripoti ya kile kilichotoka - kulia kwa kuuza nje ya bidhaa, sio sahihi hapa, kwa sababu Kifurushi ambacho kinaondoa hati moja bado kinaonekana kama chombo cha kutekelezwa. Amri ni amri iliyo waziSha256 imefutwa, kwa hivyo marekebisho yatazalisha mabaki Ushahidi hauwezi kuthibitisha.
· kamo-shared-library
Expose the content-addressed store so chat attachments can dedup
Chat attachments never went through the hashing pipeline — they wrote a private "<imgId>/<fileName>" object per upload, so the same file attached three times occupied three objects. They could not simply call uploadDocument: that flow validates client-supplied hashes, and an attachment arrives as a plain multipart with none. storeContentAddressed is the storage half on its own — hash, reuse-or-create the ImgDat, upload only if the content is new. Callers attach the returned dat to their own Img row, so an attachment stays individually access-controlled and revocable (the row is the permission; the dat is only the bytes) while the bytes are shared with every other attachment — and every document — of the same content.
· DocsService
The two byte sources for the executed artifact
LegalPinnedPdf extracts the frozen-digest rule out of the member read path so the wizard and the filed artifact read the SAME bytes through the SAME check — contentSha256 hashes the img_dats content-address triple, so the comparison costs no MinIO read and differs if and only if the bytes moved, and saveImgContent repoints img.setDat on every Docs save. Modifications are applied, because that is what the member was shown. **************** reads the FLATTENED PDF from the service that produced it. Nothing in ESigService changes: the internal endpoint already exists behind the same X-Internal-Auth + X-Org-Id pair. Re-flattening here would be a second set of signature-placement bugs over bytes ESigService has already committed to. 404 comes back as an empty Optional rather than an exception. EsignPublicService wraps flatten in try/catch { log.error }, so an envelope can sit SIGNED with a null signedBlobId forever — that is exactly what EXECUTION_INCOMPLETE names, and the caller refuses to merge an artifact missing a signature page.
· kamo-internal
Mwanachama wa kisheria-compliance ukurasa na mfuko utekelezaji mchawi
**************** is gated on identity (memberID === getMemberIdString(), compared as STRINGS because getMemberId() is lossy past MAX_SAFE_INTEGER) and includes the early `return null` the member settings page omits — that page renders its tabs for the frame before its redirect lands, which here would paint another member's package list. Never checkRight(): it returns true for everything under god mode. The wizard is the only genuinely new UI: an outer per-document step axis in orderIndex order plus a terminal attestation step. It resumes at the first incomplete step rather than at zero, refuses to skip a document (the manifest asserts the member was presented every one, in order), renders the counter from data because common.table.of is an empty string in pl, and shows a persistent awaiting-other-parties panel instead of re-running Consent -> Sign for a signer who already signed. Signable documents render the bytes ESigService flattens; acknowledge-only documents render DocsService's version-pinned bytes. A 422 from submit is surfaced verbatim — it means the server's FieldCompletion disagreed with the ported isFieldComplete, which is a port bug and must not hide behind a generic toast. Finish does NOT treat 409 as success, against the plan's sketch: LegalPackageAttestationService returns 200 with the existing attestation for an already-finished assignment, so a 409 from that endpoint means NOT_OPEN / NO_WORDING / NOT_DELIVERED / NOT_SIGNED / UNVERIFIABLE — VOIDED and SUPERSEDED among them. It re-reads the assignment and only reports success when the row is really attested.
· kamo-internal
Member execution proxy routes and the pure wizard model
Eight 1:1 routes: /api/docs/legal/member/** through forwardToApi (PATH ONLY — forwardToApi owns the query string) and /api/esig/member/** direct to ESIG_SERVICE_URL with generateOTK, because APIService forwards /api/docs/** and nothing forwards /api/esig/**. One OTK, one upstream request per route: OTKAuthFilter does getAndDelete. The PDF routes stream bytes and preserve the upstream status, so a 409 (the frozen content digest no longer matches the stored bytes) reaches the wizard as a 409 rather than as a corrupt PDF. Consent is the one route that must not parse its success body: ESigService answers it with an empty 200. Every wizard rule lives in a pure module because vitest here collects only .test.ts in the node environment. A signable document renders from the esign route — those are the bytes ESigService flattens, and what you see must be what you sign; an acknowledge-only document renders from DocsService's version-pinned route. The step axis is orderIndex order and the attestation step exists even for a package with zero documents to sign.
· kamo-internal
Port the kamo-signer signing core into kamo-internal
Every @kamo/* package is private:true with exports:"./src/index.ts" and no build step, so there is no publishable artifact — the files are copied and rewired onto relative imports. Behaviour is unchanged; only imports and i18n move. pdfjs-dist 4.10.38 with a SELF-HOSTED worker copied by a prebuild step. Not **************** that loads pdf.js 4.4.168 from cdnjs, which is a different engine and unacceptable for a legally binding surface, and the two GlobalWorkerOptions objects stay uncorrupted only because the module instances never meet. Not FieldElement.tsx either: its FieldControl renders pointerEvents:'none' previews with no value binding — a designer preview, not an input. useTranslations comes out of the four signing components: they call kamo-signer's own message keys, kamo-internal's dictionary does not have them, and new keys cannot be invented. Strings arrive as props. SIGNATURE_FONTS is a prop too rather than four next/font/google loads for one modal; the typed font id still rides through CapturedSignature.typedFont so flatten and the certificate are unaffected. Two marked deviations from a verbatim copy, both in signStore: - required-ness now skips readOnly fields. kamo-signer counted them; ESigService's FieldCompletion — the shipped server-side enforcement — does not ("if (f == null || f.readOnly() || !f.required()) continue;"). Keeping the signer copy stricter than the server would park a member on a gap they have no control to close while the server considers the envelope complete. - draft persistence guards localStorage instead of window. window is the wrong question: localStorage can be absent (server render) or throw on access (private mode, partitioned storage), and a draft must never take the wizard down with it.

Ulimwengu wako unasubiri.
Kuanza bure katika dakika. Ongeza bidhaa wakati wowote wewe ni tayari - hakuna uhamiaji, hakuna logins mpya.
Bure milele - hakuna kadi ya mkopo inahitajika