Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
725a852
feat(lab): add lab-writes Cargo feature (off by default)
Sep 13, 2026
9bd3fb1
test(lab): fail CI if any workflow enables lab-writes
Sep 13, 2026
3a0dadf
feat(lab): wire lab tool registration and dispatch (feature+env gated)
Sep 13, 2026
ca51e6c
feat(lab): windowed inventory-voucher read profile
Sep 13, 2026
3d706d2
feat(lab): lab guard scaffolding and lab_read_inventory tool
Sep 13, 2026
5ead2eb
feat(lab): add post_lab_import runtime transport (lab-writes only)
Sep 13, 2026
a978fe4
feat(lab): lab_import_masters and lab_import_vouchers (Phase 3.4/3.5)
Sep 13, 2026
a14f56c
fix(lab): distinguish Tally default masters from true collisions (Pha…
Sep 13, 2026
557d08e
fix(lab): proven-good master Create shape + explicit tally_rejected r…
Sep 14, 2026
4ea1220
fix(lab): decode entity references in read-back parsers + idempotent …
Sep 14, 2026
4f37057
fix(lab): normalise reserved-root spellings + ledger reconcile via pa…
Sep 14, 2026
efb8fa2
fix(lab): deterministic voucher marker + numeric batch order + per-vo…
Sep 14, 2026
85c3088
Merge remote-tracking branch 'origin/master' into lab/inventory-rebuild
Sep 15, 2026
9f25b21
chore(lab): reseal the compatibility surface and regenerate the Rust …
Sep 15, 2026
7e3d0bc
fix(lab): bind a field's text to the element that closes it (#403)
lamemustafa Sep 15, 2026
e8587c8
Merge remote-tracking branch 'origin/master' into lab/inventory-rebuild
Sep 15, 2026
b4ae4c5
Merge remote-tracking branch 'origin/master' into lab/inventory-rebuild
Sep 15, 2026
1bee7ef
Merge remote-tracking branch 'origin/master' into lab/inventory-rebuild
Sep 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions THIRD_PARTY_LICENSES_RUST.txt
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,10 @@ servo_arc 0.4.3
License: MIT OR Apache-2.0
Source: https://github.com/servo/stylo

sha1_smol 1.0.1
License: BSD-3-Clause
Source: https://github.com/mitsuhiko/sha1-smol

sha2 0.10.9
License: MIT OR Apache-2.0
Source: https://github.com/RustCrypto/hashes
Expand Down Expand Up @@ -10847,6 +10851,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
BSD 3-Clause "New" or "Revised" License
- alloc-stdlib 0.2.4
- aws-lc-sys 0.44.0
- sha1_smol 1.0.1

Copyright (c) <year> <owner>.

Expand Down
2 changes: 1 addition & 1 deletion docs/tally/compatibility/compatibility-matrix.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schema_version": 1,
"bridge_commit_sha": "be1c20cc3fd66fa1ece196505c69f26e555e4b8e",
"compatibility_surface_sha256": "9e5603c499d565b9fd7ea40ce4cb321270bc6472ad9a590b0b37f550e99d1fbc",
"compatibility_surface_sha256": "eee5932e77e50443ffd5bf961eeb8548e081e902574fba500a20450f1d1040b6",
"claims": [
{
"claim_id": "erp9-6-6-3-windows-education-xml-one-company",
Expand Down
14 changes: 7 additions & 7 deletions docs/tally/compatibility/compatibility-surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@
},
{
"path": "src-tauri/Cargo.lock",
"sha256": "e099a547e82382740f7f62e3f041c2523e83cb16cf0f598b92b5b07be3009f70"
"sha256": "a6c518833d11a03ddafe449ddc3b0b12043271af30cf7f81ea9be9d8e47081f2"
},
{
"path": "src-tauri/Cargo.toml",
"sha256": "d571f7a8ce0e40147bdb5a1b9d7757ada2536c22c040d408bf8ad6a668fe44a6"
"sha256": "15ff2162eb7398fcfcfeb822f40688a3bb075e1231ee8ae792f47576bf01a7c9"
},
{
"path": "src-tauri/crates/bridge-tally-core/Cargo.toml",
Expand Down Expand Up @@ -331,11 +331,11 @@
},
{
"path": "src-tauri/src/agent.rs",
"sha256": "2132f9735c9ffe79262ad84a83c99c899fde4c37a809420877e5db83dcd3bcc4"
"sha256": "7a95fa3be99ba0c25fdb8aea06c5a7e0db1321b3137c79c4722bd0ac01eb5ef1"
},
{
"path": "src-tauri/src/agent_catalog.rs",
"sha256": "242c00ddead06e2cebf46970703dc8c8f5831c096b0afd006bd844d9fb7e77f5"
"sha256": "e36e4cbe60e6355226b8779e82395ec79309213f8c0dca59aea8bbc745252601"
},
{
"path": "src-tauri/src/agent_desktop_journal.rs",
Expand All @@ -359,7 +359,7 @@
},
{
"path": "src-tauri/src/agent_read_profiles.rs",
"sha256": "cbcc6831046134158fad25ca3bbbda34f5060f0ab6d299b428f996e541b7e072"
"sha256": "f6f07dbcbce22498e4e4e6244cfd19b12bc017749ed9f0a45274f9354e2dba51"
},
{
"path": "src-tauri/src/agent_read_validation.rs",
Expand Down Expand Up @@ -663,7 +663,7 @@
},
{
"path": "src-tauri/src/tally/runtime.rs",
"sha256": "de6634ae5e09b126a4c451ca6d7e43f25e787d98f0fb94d93b78a5bc13165675"
"sha256": "9f1e388637760ddb0aa5c4de884c9f9dbc66e5714ace4cdd6bdbd2949888a81b"
},
{
"path": "src-tauri/src/tally/runtime_trial_balance.rs",
Expand Down Expand Up @@ -870,5 +870,5 @@
"sha256": "a8ac2714fecf51947f2822c8c46d7ce2e8602c732780ff60566a7771f0836f9a"
}
],
"manifest_sha256": "9e5603c499d565b9fd7ea40ce4cb321270bc6472ad9a590b0b37f550e99d1fbc"
"manifest_sha256": "eee5932e77e50443ffd5bf961eeb8548e081e902574fba500a20450f1d1040b6"
}
7 changes: 7 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ voucher-scan = ["bridge-tally-protocol/voucher-scan", "bridge-tally-transport/vo
# The harness is what actually drives a voucher scan, so it implies the scan
# machinery it needs.
live-calibration-harness = ["voucher-scan"]
# LAB-ONLY additive surface for the audit-sprint 2026-09-14 local rebuild
# workflow (Phase 3). Not default; never enable in a release or CI workflow
# (`tests/lab_writes_ci_gate.rs` fails the build if any `.github/workflows/*`
# file does). Every tool this compiles in additionally refuses at runtime
# unless `BRIDGE_LAB_WRITES=1` is set, and further refuses unless
# `BRIDGE_TALLY_PORT=9001`, `BRIDGE_LAB_TARGET_GUID` and
# `BRIDGE_LAB_DENY_GUIDS` are all present and the observed loaded-company set
# matches them. Production write guards (`agent_import_post.rs`,
# `approved_import.rs`) are never touched by this feature; it is additive.
lab-writes = []

[workspace]
members = [
Expand Down Expand Up @@ -115,7 +125,7 @@ tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["io"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4", "serde"] }
uuid = { version = "1", features = ["v4", "v5", "serde"] }
zeroize = "1"
pdf-writer = "0.15.0"

Expand Down
23 changes: 23 additions & 0 deletions src-tauri/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ mod agent_import;
pub use crate::tally::approved_import::run_confirmation;
pub(crate) use agent_import::desktop_journal_review as desktop_journal;

// LAB-ONLY additive surface (audit-sprint 2026-09-14, Phase 3). Compiled only
// behind the `lab-writes` Cargo feature (not default, never enabled in a
// release/CI workflow -- see `tests/lab_writes_ci_gate.rs`) and every tool it
// registers additionally refuses at runtime unless `BRIDGE_LAB_WRITES=1` and
// the rest of the lab guard env is present. Never modifies the production
// write guards (`agent_import.rs`/`agent_import_post.rs`/`approved_import.rs`).
#[cfg(feature = "lab-writes")]
#[path = "agent_lab.rs"]
mod lab;

#[path = "agent_catalog.rs"]
mod catalog;
#[cfg(test)]
Expand Down Expand Up @@ -639,6 +649,13 @@ impl Server {
if name == "post_import" && !self.settings.writes_enabled {
return Err("import_posting_disabled".to_string().into());
}
#[cfg(feature = "lab-writes")]
if matches!(
name,
"lab_read_inventory" | "lab_import_masters" | "lab_import_vouchers"
) {
lab::require_lab_writes_env()?;
}
validate_tool_arguments(name, args)?;
match name {
"tally_status" => {
Expand Down Expand Up @@ -680,6 +697,12 @@ impl Server {
"trial_balance" => self.trial_balance(args).await,
"read_evidence" => self.read_evidence(args).map_err(Into::into),
"egress_log" => self.egress_log(args).map_err(Into::into),
#[cfg(feature = "lab-writes")]
"lab_read_inventory" => lab::lab_read_inventory(self, args).await,
#[cfg(feature = "lab-writes")]
"lab_import_masters" => lab::lab_import_masters(self, args).await,
#[cfg(feature = "lab-writes")]
"lab_import_vouchers" => lab::lab_import_vouchers(self, args).await,
_ => Err("tool_not_found".to_string().into()),
}
}
Expand Down
45 changes: 44 additions & 1 deletion src-tauri/src/agent_catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,19 @@ pub(super) fn tool_definitions(import_enabled: bool, writes_enabled: bool) -> Va
definitions
}

#[cfg(feature = "lab-writes")]
fn lab_tools_env_enabled() -> bool {
super::lab::env_lab_writes_enabled()
}
#[cfg(not(feature = "lab-writes"))]
fn lab_tools_env_enabled() -> bool {
false
}

// Retain the internal schema while bounded change enumeration is unqualified.
pub(super) fn registered_tool_definitions(import_enabled: bool, writes_enabled: bool) -> Value {
let names = [
#[allow(unused_mut)] // only mutated when the `lab-writes` feature is compiled in
let mut names = vec![
"tally_status",
"list_companies",
"voucher_schema",
Expand All @@ -249,6 +259,12 @@ pub(super) fn registered_tool_definitions(import_enabled: bool, writes_enabled:
"read_evidence",
"egress_log",
];
#[cfg(feature = "lab-writes")]
names.push("lab_read_inventory");
#[cfg(feature = "lab-writes")]
names.push("lab_import_masters");
#[cfg(feature = "lab-writes")]
names.push("lab_import_vouchers");
Value::Array(
names
.into_iter()
Expand All @@ -257,6 +273,15 @@ pub(super) fn registered_tool_definitions(import_enabled: bool, writes_enabled:
// uncertain saved batch can still be checked safely.
.filter(|name| import_enabled || *name != "build_import_xml")
.filter(|name| writes_enabled || *name != "post_import")
// LAB-ONLY: registered only when the `lab-writes` feature is
// compiled in AND `BRIDGE_LAB_WRITES=1` is set (checked fresh on
// every catalog build, not cached at startup).
.filter(|name| {
!matches!(
name,
&"lab_read_inventory" | &"lab_import_masters" | &"lab_import_vouchers"
) || lab_tools_env_enabled()
})
.map(|name| {
let (description, input_schema) = match name {
"voucher_schema" => (
Expand Down Expand Up @@ -333,6 +358,18 @@ pub(super) fn registered_tool_definitions(import_enabled: bool, writes_enabled:
"Return bounded local metadata-only read evidence or egress receipts.",
json!({"type":"object","additionalProperties":false,"properties":{"limit":{"type":"integer","minimum":1,"default":20}}}),
),
"lab_read_inventory" => (
"LAB-ONLY. Compiled only behind the `lab-writes` feature and refuses unless BRIDGE_LAB_WRITES=1, BRIDGE_TALLY_PORT=9001, and BRIDGE_LAB_TARGET_GUID/BRIDGE_LAB_DENY_GUIDS are both set to well-formed GUIDs. This is a read: company_guid selects the company like any other read tool and is verified the same way (`company_identity_not_found`/`company_identity_ambiguous`), independent of the configured lab target -- the stronger loaded-company/deny-list guard applies only to a lab write batch, not a read. Read-only: units, godowns, stock groups and stock items (parent, base unit, opening qty/rate/value, GST/HSN fields as returned, unclassified), plus inventory entries per voucher for a date window. Reuses the same windowing and window_honoured corroboration as `vouchers`. No signed compatibility evidence exists yet for any inventory field on this Tally release/mode -- treat every value as exploratory.",
json!({"type":"object","additionalProperties":false,"required":["company_guid","from","to"],"properties":{"company_guid":{"type":"string","minLength":1},"from":{"type":"string","pattern":"^[0-9]{4}-?[0-9]{2}-?[0-9]{2}$"},"to":{"type":"string","pattern":"^[0-9]{4}-?[0-9]{2}-?[0-9]{2}$"},"offset":{"type":"integer","minimum":0,"default":0},"limit":{"type":"integer","minimum":1,"default":500}}}),
),
"lab_import_masters" => (
"LAB-ONLY (Phase 3.4). Compiled only behind `lab-writes`; refuses unless BRIDGE_LAB_WRITES=1, BRIDGE_TALLY_PORT=9001, and BRIDGE_LAB_TARGET_GUID/BRIDGE_LAB_DENY_GUIDS are set. Creates masters (units, godowns, stock groups, groups, ledgers, stock items, in that order) from the book model's `masters` section (inline `masters` or a `book_path` local JSON file). Re-verifies the loaded-company/deny-list/target-identity guard before every batch (<=200 masters). Refuses before any write if the target already carries a same-name master under any requested kind (the Create-overwrite trap, §9.4) -- `lab_master_already_exists`. Every batch is read back field-by-field (name, parent, opening balance/qty, GST fields) and the whole call stops on the first mismatch; never trusts CREATED/ERRORS alone. Group/Unit/Godown/StockGroup/StockItem XML shapes have no live capture in this repository and are UNVERIFIED for the gateway -- see the tool's module documentation.",
json!({"type":"object","additionalProperties":false,"required":["company_guid"],"properties":{"company_guid":{"type":"string","minLength":1},"masters":{"type":"object"},"book_path":{"type":"string","minLength":1}}}),
),
"lab_import_vouchers" => (
"LAB-ONLY (Phase 3.5). Compiled only behind `lab-writes`; refuses unless BRIDGE_LAB_WRITES=1, BRIDGE_TALLY_PORT=9001, and BRIDGE_LAB_TARGET_GUID/BRIDGE_LAB_DENY_GUIDS are set. Creates vouchers (Journal/Payment/Receipt/Contra plus accounting- and invoice-mode Sales/Purchase/Credit Note/Debit Note) from the book model's `vouchers` section (inline `vouchers` or a `book_path` local JSON file), sorted by date and posted in batches of at most 100. Re-verifies the loaded-company/deny-list/target-identity guard before every batch. Before sending a batch, reads its date window back and checks every voucher against a narration-marker/voucher-number plus type/date/ledger-amount fingerprint: a fully-matched batch is skipped (resume), a partially-matched batch stops with `lab_batch_partially_verified_uncertain` rather than guessing, and only an unmatched batch is sent. Every sent batch is read back the same way and the whole call stops on the first mismatch. Invoice-mode XML (`LEDGERENTRIES.LIST`/`ALLINVENTORYENTRIES.LIST`) and every type but Sales/Journal/Payment/Receipt/Contra are UNVERIFIED for the gateway -- see the tool's module documentation. `start_batch` resumes a prior call.",
json!({"type":"object","additionalProperties":false,"required":["company_guid"],"properties":{"company_guid":{"type":"string","minLength":1},"vouchers":{"type":"array"},"book_path":{"type":"string","minLength":1},"start_batch":{"type":"integer","minimum":0,"default":0}}}),
),
_ => (
"Bridge read-only Tally tool",
json!({"type":"object", "additionalProperties": false}),
Expand All @@ -342,6 +379,12 @@ pub(super) fn registered_tool_definitions(import_enabled: bool, writes_enabled:
if name == "post_import" {
tool["annotations"] = json!({"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true});
}
if name == "lab_read_inventory" {
tool["annotations"] = json!({"readOnlyHint":true,"destructiveHint":false,"idempotentHint":true,"openWorldHint":true});
}
if matches!(name, "lab_import_masters" | "lab_import_vouchers") {
tool["annotations"] = json!({"readOnlyHint":false,"destructiveHint":true,"idempotentHint":false,"openWorldHint":true});
}
tool
})
.collect(),
Expand Down
Loading