mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-07-16 15:55:16 +03:00
fix(cf-edit): reject add-childObject when object file is missing
cf-edit add-childObject was a low-level XML-manipulation operation
with no file-existence validation — callers could register a reference
to any Type.Name in Configuration.xml's ChildObjects without the
underlying file existing on disk. Platform then refused to load:
"Файл объекта не существует".
The 4 failing tests (add-objects, remove-object, add-default-role,
set-default-roles) all used this operation with fake references in
either main input or preRun, and had no way to pass verify-snapshots
because the cf-init-ed config had no actual object files.
User observation: this is the tests being wrong, not the skill.
meta-compile/role-compile/subsystem-compile already auto-register
every new object in Configuration.xml as part of their normal flow
(meta-compile.ps1:2949-3068, role-compile.ps1:667-747,
subsystem-compile.ps1:430-506). Nobody should be calling cf-edit
add-childObject to create a new object — they should be calling the
profile skill. cf-edit add-childObject is only for rare recovery
scenarios: rolled-back Configuration.xml with intact object files,
re-import from DB dump that clobbered the root but left srcfiles.
Changes:
1. cf-edit.ps1/py: Do-AddChildObject now checks that the target file
exists at {ConfigDir}/{PluralDir}/{Name}.xml before registering.
On miss, exits 1 with a message that names the expected path and
points the user at the right skill (/meta-compile, /role-compile,
or /subsystem-compile depending on type). TYPE_TO_DIR mapping for
all 44 metadata types covers irregular plurals (FilterCriteria,
BusinessProcesses, ChartsOfAccounts, ChartsOfCharacteristicTypes,
ChartsOfCalculationTypes).
2. Tests: 4 existing cases rewritten to build realistic fixtures via
meta-compile/role-compile preRun (both skills auto-register, so
the resulting Configuration.xml already references the preRun
objects). add-objects now exercises the round-trip recovery
scenario: meta-compile creates Catalog.Товары and Document.ПриходТоваров
(auto-registered) → cf-edit remove-childObject un-registers both
(files remain) → main run re-registers via add-childObject. This
tests exactly the rollback-recovery use case the operation exists for.
3. New add-missing-errors case: negative test with expectError:
"Object file not found". Verifies the new hard-error path.
4. verify-snapshots.mjs: added symmetric expectError handling (runner.mjs
already had it at line 514). If caseData.expectError is set,
expect skill to fail; check stderr substring match; skip db-load
and mark passed. Without this, negative tests would go red in
verify-snapshots even though runner.mjs accepts them.
5. SKILL.md / reference.md: documented the new constraint and the
redirection to profile skills. Kept mention of legitimate use case
(rollback recovery).
Bumped cf-edit.ps1/py v1.0→v1.1.
Verification:
- runner --filter cf-edit (PS1): 2/6 → 7/7 (6 positive + 1 negative)
- runner --filter cf-edit --runtime python: 7/7 (dual-port clean)
- verify-snapshots --skill cf-edit: 2/6 → 7/7
With this landed P3 from debug/snapshot-verify/NEXT-STEPS.md is closed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# cf-edit v1.0 — Edit 1C configuration root (Configuration.xml)
|
||||
# cf-edit v1.1 — Edit 1C configuration root (Configuration.xml)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -32,6 +32,22 @@ TYPE_ORDER = [
|
||||
"BusinessProcess", "Task", "IntegrationService",
|
||||
]
|
||||
|
||||
# Type → on-disk directory name (plural)
|
||||
TYPE_TO_DIR = {
|
||||
"Language": "Languages", "Subsystem": "Subsystems", "StyleItem": "StyleItems", "Style": "Styles",
|
||||
"CommonPicture": "CommonPictures", "SessionParameter": "SessionParameters", "Role": "Roles", "CommonTemplate": "CommonTemplates",
|
||||
"FilterCriterion": "FilterCriteria", "CommonModule": "CommonModules", "CommonAttribute": "CommonAttributes", "ExchangePlan": "ExchangePlans",
|
||||
"XDTOPackage": "XDTOPackages", "WebService": "WebServices", "HTTPService": "HTTPServices", "WSReference": "WSReferences",
|
||||
"EventSubscription": "EventSubscriptions", "ScheduledJob": "ScheduledJobs", "SettingsStorage": "SettingsStorages", "FunctionalOption": "FunctionalOptions",
|
||||
"FunctionalOptionsParameter": "FunctionalOptionsParameters", "DefinedType": "DefinedTypes", "CommonCommand": "CommonCommands", "CommandGroup": "CommandGroups",
|
||||
"Constant": "Constants", "CommonForm": "CommonForms", "Catalog": "Catalogs", "Document": "Documents",
|
||||
"DocumentNumerator": "DocumentNumerators", "Sequence": "Sequences", "DocumentJournal": "DocumentJournals", "Enum": "Enums",
|
||||
"Report": "Reports", "DataProcessor": "DataProcessors", "InformationRegister": "InformationRegisters", "AccumulationRegister": "AccumulationRegisters",
|
||||
"ChartOfCharacteristicTypes": "ChartsOfCharacteristicTypes", "ChartOfAccounts": "ChartsOfAccounts", "AccountingRegister": "AccountingRegisters",
|
||||
"ChartOfCalculationTypes": "ChartsOfCalculationTypes", "CalculationRegister": "CalculationRegisters",
|
||||
"BusinessProcess": "BusinessProcesses", "Task": "Tasks", "IntegrationService": "IntegrationServices",
|
||||
}
|
||||
|
||||
ML_PROPS = ["Synonym", "BriefInformation", "DetailedInformation", "Copyright", "VendorInformationAddress", "ConfigurationInformationAddress"]
|
||||
SCALAR_PROPS = ["Name", "Version", "Vendor", "Comment", "NamePrefix", "UpdateCatalogAddress"]
|
||||
REF_PROPS = ["DefaultLanguage"]
|
||||
@@ -171,6 +187,7 @@ def main():
|
||||
print(f"File not found: {config_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
resolved_path = os.path.abspath(config_path)
|
||||
config_dir = os.path.dirname(resolved_path)
|
||||
|
||||
xml_parser = etree.XMLParser(remove_blank_text=False)
|
||||
tree = etree.parse(resolved_path, xml_parser)
|
||||
@@ -285,6 +302,25 @@ def main():
|
||||
sys.exit(1)
|
||||
type_idx = TYPE_ORDER.index(type_name)
|
||||
|
||||
# Check that the referenced object actually exists on disk.
|
||||
# cf-edit add-childObject is a low-level operation for rare scenarios
|
||||
# (e.g. restoring a rolled-back Configuration.xml when object files are intact).
|
||||
# For creating NEW objects, meta-compile/role-compile/subsystem-compile already
|
||||
# auto-register in Configuration.xml — calling cf-edit add-childObject there is
|
||||
# unnecessary and error-prone.
|
||||
type_dir = TYPE_TO_DIR.get(type_name)
|
||||
obj_file = os.path.join(config_dir, type_dir, f"{obj_name_val}.xml")
|
||||
if not os.path.exists(obj_file):
|
||||
hint_skill = {"Subsystem": "subsystem-compile", "Role": "role-compile"}.get(type_name, "meta-compile")
|
||||
print(
|
||||
f"Object file not found: {type_dir}/{obj_name_val}.xml\n"
|
||||
f"cf-edit add-childObject only references objects that already exist on disk.\n"
|
||||
f"To create a new {type_name}, use {hint_skill} (auto-registers in Configuration.xml):\n"
|
||||
f' /{hint_skill} with {{"type":"{type_name}","name":"{obj_name_val}"}}',
|
||||
file=sys.stderr
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Dedup
|
||||
exists = False
|
||||
for child in child_objs_el:
|
||||
|
||||
Reference in New Issue
Block a user