fix(proactive): retain unnamed administrative rows and preserve accepted import snapshot

This commit is contained in:
2026-10-28 17:28:31 +02:00
parent 2f58b7d438
commit a022ed9848
6 changed files with 111 additions and 36 deletions
@@ -1667,14 +1667,16 @@ A base interna de oferta não exige transformar cada empresa numa organização
Incremento sobre o código entregue `569a1cd7`, ramo `codex/proactive-supply-qa-20261028`: **demonstrado em QA isolada, sem instalação nem importação no activo**. O relatório de conferência anterior permanece histórico: no destino activo, o uso das listas continua não demonstrado. Reutilizam-se a aceitação do percurso, o inventário de 13 ficheiros, os originais, as fontes e as provas anteriores; não se reabre a decisão da Tempo nem se encerra o estudo de mercado nacional.
Carregadas privadamente **343.041 observações**: 140.847 da lista 2014–2022, 201.917 de empresas licenciadas e 277 do cadastro turístico (duas folhas com estruturas distintas). Os quatro hashes dos originais e do CAE foram conferidos. Pré-visualização, análise Microsoft Defender sem ameaças e repetição idempotente 343.041→343.041 demonstradas. Os 139.591 registos com a mesma projecção administrativa são conservados como repetições; não representam empresas únicas nem duplicados de ficheiros por bytes. As 113.154 observações com denominações divergentes para um identificador partilhado ficam assinaladas, sem fusão automática. Contactos, documentos de identidade e NUIT não são devolvidos pela consulta.
Carregadas privadamente **361.464 observações**: 148.556 da lista 2014–2022, 212.631 de empresas licenciadas e 277 do cadastro turístico (duas folhas com estruturas distintas). Os quatro hashes dos originais e do CAE foram conferidos. Pré-visualização, análise Microsoft Defender sem ameaças e repetição idempotente 343.041→343.041 demonstradas. Os 147.165 registos com a mesma projecção administrativa são conservados como repetições; não representam empresas únicas nem duplicados de ficheiros por bytes. As 113.880 observações com denominações divergentes para um identificador partilhado ficam assinaladas, sem fusão automática. Contactos, documentos de identidade e NUIT não são devolvidos pela consulta.
Consulta interna por localização canónica (incluindo descendentes), sector CAE e actividade; resultados paginados conservam fonte, folha/linha, hash e data original. O sector provém da hierarquia SELF_ID do CAE fornecido, sem inventar correspondência com os sectores de navegação. A selecção permite comparar possível concorrência, complementaridade, parceiro ou relação por determinar, com fundamento e incertezas. O servidor recupera a origem, impede reclassificar a evidência interna como pública e conserva-a no rascunho e no dossier privado. Não transforma a lista em anúncios, promotores, clientes confirmados ou publicação de directório.
**Gurué:** o operador QA sintético 1021 consultou 410 observações distintas, comparou dois registos reais (serviços agrícolas CAE 01610 e comércio de plantas/sementes/fertilizantes CAE 47732) com a hipótese já preparada de apoio à colheita e qualidade da folha verde. A oferta possível exige verificar sobreposição e colaboração, sem presumir mercado vazio. Rascunho 402 passa de v1 a v2, mantendo integralmente as cinco evidências anteriores, a ideia e o histórico. O mesmo dossier `883c8ec3-59a3-4074-b91c-b7d4a5d88ff0` passa de contexto1 a2 por pré-visualização e confirmação idempotente, preservando título e campos deliberados. A necessidade permanece **hipótese**, sem procura, falta de oferta ou viabilidade confirmadas. Gravação, recarregamento, acesso privado e resultado vazio como **informação insuficiente** demonstrados no navegador. Isto é prova técnica, sem substituir observação/aceitação da equipa.
**Gurué:** o operador QA sintético 1021 consultou 410 observações distintas na primeira demonstração (435 após o complemento de registos sem denominação), comparou dois registos reais (serviços agrícolas CAE 01610 e comércio de plantas/sementes/fertilizantes CAE 47732) com a hipótese já preparada de apoio à colheita e qualidade da folha verde. A oferta possível exige verificar sobreposição e colaboração, sem presumir mercado vazio. Rascunho 402 passa de v1 a v2, mantendo integralmente as cinco evidências anteriores, a ideia e o histórico. O mesmo dossier `883c8ec3-59a3-4074-b91c-b7d4a5d88ff0` passa de contexto1 a2 por pré-visualização e confirmação idempotente, preservando título e campos deliberados. A necessidade permanece **hipótese**, sem procura, falta de oferta ou viabilidade confirmadas. Gravação, recarregamento, acesso privado e resultado vazio como **informação insuficiente** demonstrados no navegador. Isto é prova técnica, sem substituir observação/aceitação da equipa.
**Cobertura e pendências:** 198.112 observações sem correspondência territorial canónica única (incluindo 19 ambiguidades), 325 apenas com província e 80.796 sem designação de actividade CAE correspondente. Mantêm-se os rótulos originais e a incerteza; não há atribuição territorial aproximada nem preenchimento presumido. O cadastro de alojamento não é um censo exaustivo de hotéis/restaurantes. Situação operacional actual, capacidade, preços, procura e compromissos continuam por demonstrar. O incremento nacional é parcial; não encerra os requisitos amplos de mercado, clusters ou liquidação anual. **L8 permanece parcial e inalterado.**
**Cobertura e pendências:** 205.608 observações sem correspondência territorial canónica única (incluindo 19 ambiguidades), 336 apenas com província e 82.864 sem designação de actividade CAE correspondente. Mantêm-se os rótulos originais e a incerteza; não há atribuição territorial aproximada nem preenchimento presumido. O cadastro de alojamento não é um censo exaustivo de hotéis/restaurantes. Situação operacional actual, capacidade, preços, procura e compromissos continuam por demonstrar. O incremento nacional é parcial; não encerra os requisitos amplos de mercado, clusters ou liquidação anual. **L8 permanece parcial e inalterado.**
Sete testes dirigidos/62 asserções, TypeScript e compilação QA passaram. As duas tabelas internas têm RLS forçada; teste com identidade sem privilégios confirma ocultação sem contexto de actor e leitura com contexto autorizado. Conferidos os hashes de todas as linhas anteriores nos sete conjuntos do inventário: só o rascunho402 e o mesmo dossier sofreram o delta intencional; Songo401, projectos, territórios, sectores, documentos e cadeias foram conservados. Provas fiscais aceites reutilizadas, sem repetir as suas suites.
Actualizados os cinco requisitos existentes `RF-CLU-003`, `MASTER-471012ab-0010`, `M2-d0c06a75-0147`, `M2-d0c06a75-0151` e `MASTER-1f0a4cde-0002`, preservando o restante estado e pendências. Nenhuma matriz ou backlog novo. A entrega activa futura mantém conferência de capacidade/compatibilidade com main, backup/restauro fresco, operação autenticada e delta instalado. Relatório: `docs/investmoz/unification/proactive-existing-supply-qa-20261028.json`.
Complemento final: as 18.423 linhas empresariais sem denominação social também foram conservadas, com essa lacuna explícita e sem reutilizar o nome pessoal do requerente. As 343.041 observações da primeira fotografia permaneceram idênticas; extensão e repetição idempotente atingem 361.464 sem substituir registos, alterar as comparações ou rever as provas aceites. Os exemplares e as fotografias anteriores foram preservados.
@@ -37,17 +37,18 @@
}
],
"source_rows": {
"companies.xlsx": 140847,
"licensed.xlsx": 201917,
"companies.xlsx": 148556,
"licensed.xlsx": 212631,
"tourism.xlsx": 277
},
"catalog": {
"records": 343041,
"repeated_projection": 139591,
"identity_conflicts": 113154,
"activity_unclassified": 80796,
"territory_unmapped": 198112,
"province_only": 325
"records": 361464,
"repeated_projection": 147165,
"identity_conflicts": 113880,
"activity_unclassified": 82864,
"territory_unmapped": 205608,
"province_only": 336,
"unnamed_observations": 18423
},
"duplicate_meaning": "same projected administrative fields including entity identifier, licence/process,date,place,activity,name;all source sightings retained,not a unique-company count or byte-identical-file claim",
"classification": {
@@ -76,7 +77,27 @@
"RLS": "forced on both;non-owner QA policy proof:0 rows without actor/read context;authorized context recovers row",
"contacts_identity_documents_nuit_not_returned_by_query": true,
"no_automatic_directory_publication": true,
"no_promoters_or_user_organizations_created": true
"no_promoters_or_user_organizations_created": true,
"extension_first": {
"before": 343041,
"after": 361464,
"sha256": "dec9db62a52a8bac5f1e2675aecbeb4239f2b1f4fa521db01693c32bdfeef01e",
"extension": true,
"previous_snapshot": "6a9c820c73a8c535ed3cdfc03db0311edf0300f1f97ade75f0fce3d3c84a02e1",
"publication": false,
"database": "proactive_investment_20261027_test",
"port": 55441
},
"extension_replay": {
"before": 361464,
"after": 361464,
"sha256": "dec9db62a52a8bac5f1e2675aecbeb4239f2b1f4fa521db01693c32bdfeef01e",
"extension": true,
"previous_snapshot": "6a9c820c73a8c535ed3cdfc03db0311edf0300f1f97ade75f0fce3d3c84a02e1",
"publication": false,
"database": "proactive_investment_20261027_test",
"port": 55441
}
},
"proactive_delta": "private authenticated territory query,CAE sector/activity filters,pagination,source/row/date/hash;select existing observations,compare competition/complementarity/possible partner/uncertain with rationale and limitations;server resolves source lineage,keeps list-derived evidence internal;save/reload and private dossier preserve references",
"gurue": {
@@ -107,7 +128,15 @@
"context_revision": 2,
"deliberate_dossier_title_and_fields_preserved": true,
"prior_source_v1_history_retained": true,
"songo_401_unchanged": true
"songo_401_unchanged": true,
"refreshed_offer_query_total": 435,
"initial_browser_proof_retained": true,
"dossier_comparisons_visible": {
"existing_dossier_comparison_1_visible": true,
"existing_dossier_comparison_2_visible": true,
"read_only_after_commit": true,
"errors": []
}
},
"verification": {
"delta_tests": 7,
@@ -160,9 +189,9 @@
},
"remaining": [
"No installation or import in active portal;there the list-backed use remains not demonstrated until delivery.",
"198112 observations lack a unique canonical territorial match,including19 ambiguous names;325 have province only. Raw source territory remains available in private originals.",
"80796 observations lack an exact supplied CAE activity label;do not impute sector/activity.",
"113154 observations have differing names for a shared valid-shaped identifier;identity needs reconciliation,not automatic merger.",
"205608 observations lack a unique canonical territorial match,including19 ambiguous names;336 have province only. Raw source territory remains available in private originals.",
"82864 observations lack an exact supplied CAE activity label;do not impute sector/activity.",
"113880 observations have differing names for a shared valid-shaped identifier;identity needs reconciliation,not automatic merger.",
"Historical administrative records do not establish current operating status,capacity,prices,demand,contracts or market vacancy.",
"National mapping/classification completeness and actual team use remain partial;Gurué proof does not close all market-study/cluster requirements.",
"Active delivery requires capability/compatibility review,fresh backup+restore,authenticated operation and installed delta check through the existing Git procedure."
@@ -176,7 +205,26 @@
"outputs/proactive-supply-20261028/gurue-ui-proof.json",
"outputs/proactive-supply-20261028/preservation-proof.json",
"outputs/proactive-supply-20261028/tests-receipt.json",
"outputs/proactive-supply-20261028/dossier-visual-proof.json"
"outputs/proactive-supply-20261028/dossier-visual-proof.json",
"outputs/proactive-supply-20261028/supply-extension-preservation.json",
"outputs/proactive-supply-20261028/refreshed-gurue-query.json"
],
"L8": "unchanged,partial;this increment has no fiscal calculation change"
"L8": "unchanged,partial;this increment has no fiscal calculation change",
"supplement": {
"previous_snapshot_records": 343041,
"added_unnamed_source_rows": 18423,
"placeholder": "Denominação não indicada;never the personal applicant name",
"all_previous_observations_unchanged": {
"old_observations_unchanged": 343041,
"new_observations": 18423
},
"preview_both_hashes_bound": {
"previous_observations_unchanged": 343041,
"new_observations": 18423,
"no_replacement": true,
"previous_sha256": "6a9c820c73a8c535ed3cdfc03db0311edf0300f1f97ade75f0fce3d3c84a02e1",
"sha256": "dec9db62a52a8bac5f1e2675aecbeb4239f2b1f4fa521db01693c32bdfeef01e"
},
"original_source_rows_accounted_for": "148556 companies+212631 licensed nonempty data rows,excluding headers;277 tourism establishment sightings from two structures"
}
}
@@ -7225,14 +7225,16 @@ A base interna de oferta não exige transformar cada empresa numa organização
Incremento sobre o código entregue `569a1cd7`, ramo `codex/proactive-supply-qa-20261028`: **demonstrado em QA isolada, sem instalação nem importação no activo**. O relatório de conferência anterior permanece histórico: no destino activo, o uso das listas continua não demonstrado. Reutilizam-se a aceitação do percurso, o inventário de 13 ficheiros, os originais, as fontes e as provas anteriores; não se reabre a decisão da Tempo nem se encerra o estudo de mercado nacional.
Carregadas privadamente **343.041 observações**: 140.847 da lista 2014–2022, 201.917 de empresas licenciadas e 277 do cadastro turístico (duas folhas com estruturas distintas). Os quatro hashes dos originais e do CAE foram conferidos. Pré-visualização, análise Microsoft Defender sem ameaças e repetição idempotente 343.041→343.041 demonstradas. Os 139.591 registos com a mesma projecção administrativa são conservados como repetições; não representam empresas únicas nem duplicados de ficheiros por bytes. As 113.154 observações com denominações divergentes para um identificador partilhado ficam assinaladas, sem fusão automática. Contactos, documentos de identidade e NUIT não são devolvidos pela consulta.
Carregadas privadamente **361.464 observações**: 148.556 da lista 2014–2022, 212.631 de empresas licenciadas e 277 do cadastro turístico (duas folhas com estruturas distintas). Os quatro hashes dos originais e do CAE foram conferidos. Pré-visualização, análise Microsoft Defender sem ameaças e repetição idempotente 343.041→343.041 demonstradas. Os 147.165 registos com a mesma projecção administrativa são conservados como repetições; não representam empresas únicas nem duplicados de ficheiros por bytes. As 113.880 observações com denominações divergentes para um identificador partilhado ficam assinaladas, sem fusão automática. Contactos, documentos de identidade e NUIT não são devolvidos pela consulta.
Consulta interna por localização canónica (incluindo descendentes), sector CAE e actividade; resultados paginados conservam fonte, folha/linha, hash e data original. O sector provém da hierarquia SELF_ID do CAE fornecido, sem inventar correspondência com os sectores de navegação. A selecção permite comparar possível concorrência, complementaridade, parceiro ou relação por determinar, com fundamento e incertezas. O servidor recupera a origem, impede reclassificar a evidência interna como pública e conserva-a no rascunho e no dossier privado. Não transforma a lista em anúncios, promotores, clientes confirmados ou publicação de directório.
**Gurué:** o operador QA sintético 1021 consultou 410 observações distintas, comparou dois registos reais (serviços agrícolas CAE 01610 e comércio de plantas/sementes/fertilizantes CAE 47732) com a hipótese já preparada de apoio à colheita e qualidade da folha verde. A oferta possível exige verificar sobreposição e colaboração, sem presumir mercado vazio. Rascunho 402 passa de v1 a v2, mantendo integralmente as cinco evidências anteriores, a ideia e o histórico. O mesmo dossier `883c8ec3-59a3-4074-b91c-b7d4a5d88ff0` passa de contexto1 a2 por pré-visualização e confirmação idempotente, preservando título e campos deliberados. A necessidade permanece **hipótese**, sem procura, falta de oferta ou viabilidade confirmadas. Gravação, recarregamento, acesso privado e resultado vazio como **informação insuficiente** demonstrados no navegador. Isto é prova técnica, sem substituir observação/aceitação da equipa.
**Gurué:** o operador QA sintético 1021 consultou 410 observações distintas na primeira demonstração (435 após o complemento de registos sem denominação), comparou dois registos reais (serviços agrícolas CAE 01610 e comércio de plantas/sementes/fertilizantes CAE 47732) com a hipótese já preparada de apoio à colheita e qualidade da folha verde. A oferta possível exige verificar sobreposição e colaboração, sem presumir mercado vazio. Rascunho 402 passa de v1 a v2, mantendo integralmente as cinco evidências anteriores, a ideia e o histórico. O mesmo dossier `883c8ec3-59a3-4074-b91c-b7d4a5d88ff0` passa de contexto1 a2 por pré-visualização e confirmação idempotente, preservando título e campos deliberados. A necessidade permanece **hipótese**, sem procura, falta de oferta ou viabilidade confirmadas. Gravação, recarregamento, acesso privado e resultado vazio como **informação insuficiente** demonstrados no navegador. Isto é prova técnica, sem substituir observação/aceitação da equipa.
**Cobertura e pendências:** 198.112 observações sem correspondência territorial canónica única (incluindo 19 ambiguidades), 325 apenas com província e 80.796 sem designação de actividade CAE correspondente. Mantêm-se os rótulos originais e a incerteza; não há atribuição territorial aproximada nem preenchimento presumido. O cadastro de alojamento não é um censo exaustivo de hotéis/restaurantes. Situação operacional actual, capacidade, preços, procura e compromissos continuam por demonstrar. O incremento nacional é parcial; não encerra os requisitos amplos de mercado, clusters ou liquidação anual. **L8 permanece parcial e inalterado.**
**Cobertura e pendências:** 205.608 observações sem correspondência territorial canónica única (incluindo 19 ambiguidades), 336 apenas com província e 82.864 sem designação de actividade CAE correspondente. Mantêm-se os rótulos originais e a incerteza; não há atribuição territorial aproximada nem preenchimento presumido. O cadastro de alojamento não é um censo exaustivo de hotéis/restaurantes. Situação operacional actual, capacidade, preços, procura e compromissos continuam por demonstrar. O incremento nacional é parcial; não encerra os requisitos amplos de mercado, clusters ou liquidação anual. **L8 permanece parcial e inalterado.**
Sete testes dirigidos/62 asserções, TypeScript e compilação QA passaram. As duas tabelas internas têm RLS forçada; teste com identidade sem privilégios confirma ocultação sem contexto de actor e leitura com contexto autorizado. Conferidos os hashes de todas as linhas anteriores nos sete conjuntos do inventário: só o rascunho402 e o mesmo dossier sofreram o delta intencional; Songo401, projectos, territórios, sectores, documentos e cadeias foram conservados. Provas fiscais aceites reutilizadas, sem repetir as suas suites.
Actualizados os cinco requisitos existentes `RF-CLU-003`, `MASTER-471012ab-0010`, `M2-d0c06a75-0147`, `M2-d0c06a75-0151` e `MASTER-1f0a4cde-0002`, preservando o restante estado e pendências. Nenhuma matriz ou backlog novo. A entrega activa futura mantém conferência de capacidade/compatibilidade com main, backup/restauro fresco, operação autenticada e delta instalado. Relatório: `docs/investmoz/unification/proactive-existing-supply-qa-20261028.json`.
Complemento final: as 18.423 linhas empresariais sem denominação social também foram conservadas, com essa lacuna explícita e sem reutilizar o nome pessoal do requerente. As 343.041 observações da primeira fotografia permaneceram idênticas; extensão e repetição idempotente atingem 361.464 sem substituir registos, alterar as comparações ou rever as provas aceites. Os exemplares e as fotografias anteriores foram preservados.
File diff suppressed because one or more lines are too long
+12 -3
View File
@@ -65,6 +65,7 @@ def main():
p.add_argument('--sources', required=True)
p.add_argument('--output', required=True)
p.add_argument('--only-tourism', action='store_true', help='Rebuild only tourism in an existing private preview')
p.add_argument('--only-missing-names', action='store_true', help='Add nonempty company rows without a social denomination; never substitute the personal applicant name')
a = p.parse_args()
output = Path(a.output)
output.mkdir(parents=True, exist_ok=True)
@@ -101,7 +102,8 @@ def main():
counts = {}
if a.only_tourism:
db.execute('delete from records where source_hash=?',(SOURCES['tourism.xlsx'][0],))
for alias in (('tourism.xlsx',) if a.only_tourism else ('companies.xlsx','licensed.xlsx','tourism.xlsx')):
aliases=('companies.xlsx','licensed.xlsx') if a.only_missing_names else (('tourism.xlsx',) if a.only_tourism else ('companies.xlsx','licensed.xlsx','tourism.xlsx'))
for alias in aliases:
source_hash, _, period = SOURCES[alias]
count = 0
province = ''
@@ -130,8 +132,13 @@ def main():
continue
old = alias == 'companies.xlsx'
name = c.get('L','')
if not name:
if a.only_missing_names and name:
continue
if not any(c.values()):
continue
# A missing social denomination is a data gap, not a reason to
# discard an authorised source row or publish an applicant's name.
name = name or 'Denominação não indicada'
province, district = c.get('Y' if old else 'V',''), c.get('X' if old else 'U','')
cae = c.get('S' if old else 'O','')
activity = labels.get(cae, '')
@@ -156,12 +163,14 @@ def main():
db.commit()
counts[alias] = count
print(json.dumps({'source':alias,'rows_processed':count}), flush=True)
db.execute('update records set identity_conflict=1 where entity_key in (select entity_key from records group by entity_key having count(distinct lower(name))>1)')
db.execute('update records set identity_conflict=0')
db.execute("update records set identity_conflict=1 where entity_key in (select entity_key from records where name<>'Denominação não indicada' group by entity_key having count(distinct lower(name))>1)")
db.commit()
counts={alias:db.execute('select count(*) from records where source_hash=?',(SOURCES[alias][0],)).fetchone()[0] for alias in ('companies.xlsx','licensed.xlsx','tourism.xlsx')}
manifest.update(rows_processed=counts, records=db.execute('select count(*) from records').fetchone()[0],
duplicate_sightings=db.execute('select count(*) from records where duplicate_of is not null').fetchone()[0],
conflicting_identity_sightings=db.execute('select count(*) from records where identity_conflict=1').fetchone()[0],
unnamed_observations=db.execute("select count(*) from records where name='Denominação não indicada'").fetchone()[0],
unclassified_activity=db.execute("select count(*) from records where activity=''").fetchone()[0],
unresolved_territory=db.execute("select count(*) from records where district='' or province=''").fetchone()[0])
db.close()
+20 -6
View File
@@ -23,7 +23,7 @@ if($operation==='schema'){
}
echo "QA schema available; no existing table altered.\n";exit;
}
if($operation==='import'){
if(in_array($operation,['import','extend'],true)){
$dir=$out.'/private-v2';
$manifest=json_decode(file_get_contents($dir.'/preview.json'),true,512,JSON_THROW_ON_ERROR);
$scan=json_decode(file_get_contents($out.'/private/scan.json'),true,512,JSON_THROW_ON_ERROR);
@@ -31,11 +31,25 @@ if($operation==='import'){
$file=$dir.'/observations.jsonl';$hash=hash_file('sha256',$file);
if(($argv[2]??'')!==$hash)throw new RuntimeException('Review preview and supply exact JSONL hash');
$before=DB::table('proactive_supply_observations')->count();
DB::transaction(function()use($manifest,$file,$hash){
$extension=$operation==='extend';$previousHash=$argv[3]??null;
if($extension){
$delta=json_decode(file_get_contents($dir.'/extension-preview.json'),true,512,JSON_THROW_ON_ERROR);
if(($delta['sha256']??null)!==$hash||($delta['previous_sha256']??null)!==$previousHash||empty($delta['no_replacement'])||$delta['previous_observations_unchanged']+$delta['new_observations']!==$manifest['records'])throw new RuntimeException('Extension must prove all previously accepted observations unchanged and bind both snapshot hashes.');
}
DB::transaction(function()use($manifest,$file,$hash,$extension,$previousHash){
DB::select("select pg_advisory_xact_lock(hashtext('proactive-supply-import-v1'))");
foreach($manifest['sources']as$s){
$existing=DB::table('proactive_supply_sources')->where('sha256',$s['sha256'])->first();
if($existing && data_get(json_decode($existing->receipt,true),'transport_sha256')!==$hash)throw new RuntimeException('Existing source has a different prepared classification snapshot; preserve it and prepare an explicit mapping delta instead of silently replacing observations.');
if($existing && data_get(json_decode($existing->receipt,true),'transport_sha256')!==($extension?$previousHash:$hash))throw new RuntimeException('Existing source has a different prepared classification snapshot; preserve it and prepare an explicit mapping delta instead of silently replacing observations.');
if($extension){
if(!$existing)throw new RuntimeException('An extension must reference the already imported originals.');
$receipt=json_decode($existing->receipt,true);
$history=$receipt['extensions']??[];
if(!in_array($hash,array_column($history,'transport_sha256'),true)){
$history[]=['transport_sha256'=>$hash,'rows_in_extended_preview'=>$manifest['records'],'at'=>now()->toIso8601String(),'operation'=>'append previously unrepresented source rows;existing observations immutable'];
DB::table('proactive_supply_sources')->where('sha256',$s['sha256'])->update(['receipt'=>json_encode([...$receipt,'extensions'=>$history])]);
}
}
DB::table('proactive_supply_sources')->insertOrIgnore(['sha256'=>$s['sha256'],'name'=>$s['name'],'period'=>$s['period'],'limitations'=>$manifest['limitations'],'receipt'=>json_encode(['source'=>$s,'preview_rows'=>$manifest['records'],'transport_sha256'=>$hash,'scan'=>'Defender custom scan, no threats, hash-specific receipt','scope'=>'isolated QA, internal analysis only']),'imported_at'=>now()]);
}
$f=fopen($file,'rb');$batch=[];$n=0;
@@ -48,7 +62,7 @@ if($operation==='import'){
fclose($f);if($batch)DB::table('proactive_supply_observations')->insertOrIgnore($batch);
if($n!==$manifest['records'])throw new RuntimeException('Incomplete candidate');
});
$receipt=['before'=>$before,'after'=>DB::table('proactive_supply_observations')->count(),'sha256'=>$hash,'publication'=>false,'database'=>'proactive_investment_20261027_test','port'=>55441];
file_put_contents($out.'/import-'.($before?'replay':'first').'.json',json_encode($receipt,JSON_PRETTY_PRINT));echo json_encode($receipt)."\n";exit;
$receipt=['before'=>$before,'after'=>DB::table('proactive_supply_observations')->count(),'sha256'=>$hash,'extension'=>$extension,'previous_snapshot'=>$previousHash,'publication'=>false,'database'=>'proactive_investment_20261027_test','port'=>55441];
file_put_contents($out.'/import-'.($extension?'extension':($before?'replay':'first')).'.json',json_encode($receipt,JSON_PRETTY_PRINT));echo json_encode($receipt)."\n";exit;
}
throw new RuntimeException('Use geography, schema, import <reviewed hash>, or test; exclusively isolated QA.');
throw new RuntimeException('Use geography,schema,import <reviewed hash>,extend <reviewed hash> <previous snapshot hash>,or test;exclusively isolated QA.');